【问题标题】:How to communicate between controllers in Ember.js如何在 Ember.js 中的控制器之间进行通信
【发布时间】:2015-10-07 18:48:18
【问题描述】:

我想创建一个页面,在左侧我有固定视图(一些过滤器),这些视图应用于右侧的结果。

例如,左侧是按类型、标题、创作年份过滤电影的过滤器...... 右侧是不同的图表和表格,它们会根据所选的过滤器进行更新。

所以我想在左边有一个固定的视图,然后在右边有一个出口,它会根据路线而改变。

这是正确的方法吗?如果是,我不知道如何将过滤器更改传达给右侧的控制器。

这个 JSFiddle 显示了类似的设置:http://jsfiddle.net/DEHcK/2/

在 ContentRoute -> setupController 中,我得到了 NavigationController 的一个实例,但是我不知道如何更改 someValue。

在上面的例子中,ContentController如何获取NavigationController中someValue的变化呢?

这是实现我在 ember 中描述的应用程序的正确方法吗?

JavaScript:


    window.App = Ember.Application.create();

    App.Router.map(function () {
        this.route('content');
    });

    App.ContentRoute = Ember.Route.extend({
        setupController: function (controller) {
            controller.set('navigationController', this.controllerFor('navigation'));
        }
    });

    App.ContentController = Ember.ObjectController.extend({
        navigationController: null,
        observerOfSomeValue: function () {
            this.set('observedValue', this.get('navigationController.someValue'));
        }.observes('navigationController', 'navigationController.someValue'),
        observedValue: null
    });

    App.IndexRoute = Ember.Route.extend({
        redirect: function () {
            this.transitionTo('content');
        }
    });

    App.NavigationView = Ember.View.extend({
        init: function () {
            this._super();
            this.set('controller', this.get('parentView.controller').controllerFor('Navigation'));
        },
        templateName: "navigation"
    });

    App.NavigationController = Ember.ObjectController.extend({
        someValue: 'xx',
        observedValue: null,
        observerOfSomeValue: function () {
            this.set('observedValue', this.someValue);
        }.observes('someValue')
    });

HTML:

<script type="text/x-handlebars" data-template-name="application" >
    <div>
        {{view App.NavigationView}}
    </div>
    <div>
        {{ outlet }}
    </div>
</script>

<script type="text/x-handlebars" data-template-name="navigation" >
    Change {{view Ember.TextField valueBinding="controller.someValue"}}
    <div>observed value in same view: {{controller.observedValue}}</div>
</script>

<script type="text/x-handlebars" data-template-name="content" >
    <div style="margin-top: 2em">
        observed value in another view: {{observedValue}}
    </div>
</script>

【问题讨论】:

标签: ember.js


【解决方案1】:

我已经继续并 created you a JSFiddle 完成了您所追求的基本实现。不过,我认为值得一试,以便掌握 Ember。

路由器

此时我们只是配置了我们的IndexRoute,我们将所有歌曲存储在{{outlet}}

App.IndexRoute = Ember.Route.extend({
    setupController: function(controller) {
        controller.set('content', [
            Ember.Object.create({ title: 'Stairway to Heaven', genre: 'Metal' }),
            Ember.Object.create({ title: 'Ratts of the Capital', genre: 'Post Rock' }),
            Ember.Object.create({ title: 'Wonderwall', genre: 'Brit Pop' }),
            Ember.Object.create({ title: 'Last Flowers', genre: 'Indie Rock' })
        ]);
    }
});

这段代码很有可能会被对后端 Ruby/PHP 的某种 AJAX 调用所取代。目前,我们将让IndexRoute 负责设置控制器(因此setupController)。这个责任也可以由控制器本身承担,并且抽象出 AJAX 调用可能是一个好主意,因为您将有许多类似的 AJAX 调用。

您也可以决定use Ember's DataStore,这将再次更改控制器的实现。

索引控制器

接下来我们要设置我们的IndexController(实际上是我们的SongsController),我们希望这个控制器有两个职责:

  1. 存储歌曲(也可能从后端获取歌曲);
  2. 根据传入的过滤器过滤歌曲。

为此,我们创建一个computed property 来过滤掉内容,因为我们不想直接操作特殊的content 数组。

App.IndexController = Ember.ArrayController.extend({
    content: [],
    excludeGenres: [],

    filteredContent: function() {
        var excludeTheseGenres = this.get('excludeGenres').mapProperty('genre');
        return this.get('content').filter(function(model) {
            return Boolean(jQuery.inArray(model.get('genre'), excludeTheseGenres) === -1);
        });
    }.property('excludeGenres.length', 'content.length')
});

excludeGenres 将采用一系列流派对象。例如,如果excludeGenres 中包含“Post Rock”,那么我们将不会显示任何“Post Rock”相关歌曲,但如果它不存在,那么我们将显示它们! IndexController 没有维护这个数组的责任,但是它有责任在这个数组更新时过滤它的内容。

类型控制器

可能是我们小应用程序中最令人困惑的控制器,因为它实际上并没有自己的任何内容,而是依赖于IndexController 的内容。

App.GenresController = Ember.ObjectController.extend({
    needs: ['index'],

    genres: function() {
        return this.get('controllers.index').mapProperty('genre').uniq();        
    }.property('controllers.index.length'),

    toggle: function(genreName) {
        var indexController     = this.get('controllers.index'),
            genres              = indexController.get('excludeGenres'),
            genre               = indexController.findProperty('genre', genreName);

        if (genres.findProperty('genre', genreName)) {
            genres.removeObject(genre);
            return;
        }

        genres.pushObject(genre);
    }
});

类型控制器的职责可以这样定义:

  1. 监控IndexControllercontent数组,并在其长度变化时获取唯一的流派名称;
  2. 当用户单击列表中的流派以包含/排除它时填充excludeGenres 数组。

要调用toggle 方法,我们需要在类型视图中指定一个动作,以便在单击时调用它:&lt;a {{action "toggle" genre}}&gt;{{genre}}&lt;/a&gt;。现在,每当用户点击一个流派时,toggle 方法就会被调用,流派名称作为第一个参数传递。

一旦我们进入toggle 方法,它将确定该类型是否已被排除。如果它被排除,那么它将被删除,反之亦然。一旦我们添加/删除了流派,filteredContent 计算属性将再次触发,索引视图将无缝更新。

GenresController 实际上没有自己的内容的原因是,当两者有关系时,管理两个content 数组似乎很愚蠢。由于可以从应用程序中存在的歌曲中确定流派,因此控制器可以从该列表中收集信息,然后只需提取所需的信息——在我们的例子中,就是流派。

这样,如果添加/删除一首歌曲,则流派列表可以保持同步。

结论

但是,我认为您最初问题的答案是,为了让控制器相互通信,您需要指定它需要(使用needs)。

【讨论】:

  • 哇@Wildhoney,这很有帮助。非常感谢。您的回复中有很多有用的东西。我还浏览了您的大部分其他答案并学到了很多东西。
  • 我很高兴它有帮助!有任何问题,请追问。
【解决方案2】:

用于 Ember 控制器的needs 已被弃用,并在 Ember 2.0 中被删除——看看它的Deprecation Guide

在这里复制/粘贴指南,而不是写:

Ember.Controller.extend({
  needs: ['comments'],
  newComments: Ember.computed.alias('controllers.comments.newest')
});

你应该写:

Ember.Controller.extend({
  comments: Ember.inject.controller(),
  newComments: Ember.computed.alias('comments.newest')
});

Ember.inject.controller() 将懒惰地查找与分配给它的键同名的控制器 - 在本例中为 comments 控制器。更多信息,请参阅the API documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 2015-02-27
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 2016-04-14
    相关资源
    最近更新 更多