如何在Ember.js中的控制器之间进行通信

时间:2013-02-13 20:56:59

标签: ember.js

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

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

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

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

这个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>

2 个答案:

答案 0 :(得分:25)

我已经完成了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. 根据传入的歌曲过滤歌曲。
  3. 为此我们创建了一个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将采用一系列流派对象。例如,如果“Post Rock”包含在excludeGenres中,那么我们将不会显示任何“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. 监控content的{​​{1}}数组,并在其长度发生变化时获取唯一的流派名称;
    2. 当用户点击列表中的某个类型以包含/排除它时,填充IndexController数组。
    3. 要调用excludeGenres方法,我们需要在我们的流派视图中指定一个操作,以便在单击时调用它:toggle。现在,只要用户点击某个流派,就会调用<a {{action "toggle" genre}}>{{genre}}</a>方法,并将该流派名称作为第一个参数传递。

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

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

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

      <强>结论

      我认为原始问题的答案是,为了让控制器彼此进行通信,您需要指定需要的内容(使用needs

答案 1 :(得分:1)

对于Ember Controllers的

needs已被弃用,并已在Ember 2.0中删除 - 请查看其Working Fiddle

要在此复制/粘贴指南,而不是写:

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控制器。有关更多信息,请参阅Deprecation Guide