2013-02-13 90 views
8

我想创建一个页面,在左侧我有固定的视图(一些过滤器),它们适用于右侧的结果。如何在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> 
+0

感谢@Wildhoney,这是一个更新的小提琴。 http://jsfiddle.net/DEHcK/3/ – 2013-02-14 10:36:07

回答

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' }) 
     ]); 
    } 
}); 

这段代码很可能会被某种类型的AJAX调用替换为您的后端Ruby/PHP。暂时,我们会给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将采用一组流派对象。例如,如果“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阵列IndexController的并获取特有的流派名称时其长度变化;
  2. 当用户点击列表中的流派以包含/排除流派时,填充excludeGenres阵列。

要让称为toggle方法,我们需要指定我们流派观点的动作来调用它单击时:<a {{action "toggle" genre}}>{{genre}}</a>。现在,每当用户点击一个流派时,将调用toggle方法,并将流派名称作为第一个参数传递。

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

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

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

结论

我觉得然而回答你原来的问题是,是为了让控制器能够彼此进行通信,你需要指定它需要(与needs)。

+0

Wow @Wildhoney,帮助很大。非常感谢你。你的回复中有很多有用的东西。我也经历了大部分其他答案并学到了很多东西。 – 2013-02-14 10:47:25

+0

我很高兴它帮助!有任何问题,请询问。 – Wildhoney 2013-02-14 13:32:01

1

needs对于Ember控制器已被弃用,并在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

相关问题