8

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

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

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

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

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

2 回答 2

25

我已经继续为您创建了一个 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 调用。

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

索引控制器

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

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

为此,我们创建了一个计算属性来过滤掉内容,因为我们不想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's 的内容。

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计算属性将再次被触发,并且索引视图将无缝更新。

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

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

结论

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

于 2013-02-14T01:09:01.083 回答
1

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

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

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

于 2015-10-07T10:48:18.853 回答