【问题标题】:Ember.js filtering data from a dynamic routeEmber.js 从动态路由中过滤数据
【发布时间】:2013-08-29 18:02:24
【问题描述】:

我正在尝试使用动态路由根据搜索字符串过滤数据。当从控制器使用transitionToRoute 函数时,来自路由的模型数据会正确返回到视图,但是当直接导航到 url 或刷新页面时,所有的 forEach 调用都不会被执行,因为数据的长度在模型为 0。

我有一种感觉,这是因为数据是异步加载的,但我不确定如何延迟 forEach 循环和视图的呈现,直到 find 的承诺得到解决并且 forEach 循环已经完成。

这是我的路由器的model功能:

model : function( params ){
    var lists = App.List.find( ), //gets all the lists
        query = params.query, //query string from url
        re = new RegExp( query, 'i' );

    this.set( 'query', query );

    return lists.forEach( function( list ){
        var cards = list.get( 'cards' ).forEach( function( card ){

            //the view has a class bound to the hide property of each card
            card.set( 'hide',
                ( query.length ) ? !( re.test( card.get( 'description' ) ) ) : false
            );
        } );

        return list;
    });
}

当用户使用带有#/search/red 查询字符串的url 访问应用程序时,我只希望返回其中包含“红色”的卡片。

【问题讨论】:

  • 是的,这绝对是因为异步调用。您可以创建自己的承诺。但是这段代码看起来有点难看。我建议在包含列表的控制器上设置搜索查询。然后,此控制器可以使用计算属性过滤您的列表。您的方法非常必要,而 Ember 想要推广声明式编程。
  • @mavilein 谢谢,我从这条路开始,但是我在将查询参数传递到控制器时遇到问题。我很乐意看到您的评论示例作为答案(如果还有其他问题,我可以投票)
  • 为什么不直接将参数传递给find() 方法并让后端完成工作(当然,除非您使用的是夹具数据)?

标签: javascript ember.js


【解决方案1】:

我刚刚重新发现了这个问题,这是我尝试给出的答案。正如我在评论中已经提到的,这是我的基本想法:

具有计算属性:

  • 不要在模型钩子中进行过滤,只返回列表。
  • 在 Route 的控制器上设置查询(名为 SearchController?)
  • 在控制器中作为计算属性进行过滤

使用观察者:(更接近您的原始代码)

  • 不要在模型钩子中进行过滤,只返回列表。
  • 在 Route 的控制器上设置查询(名为 SearchController?)
  • 采用逻辑从模型挂钩中隐藏卡片并将其作为控制器上的观察者实现

最好的方法是使用计算属性,但我不确定如何去做(Ember 团队指出计算属性通常会带来更好的代码)。因此,这里是使用观察者方法的代码的粗略草图。这应该可以作为一个开始:

路线:

model : function( params ){
    this.set( 'query', params.query );
    return App.List.find(); //gets all the lists
},
setupController : function(controller, model) {
    this._super(controller, model);
    // setupController is a good location to setup your controller
    controller.set("query", this.get("query"));
}

控制器:

App.SearchController = Ember.ArrayController.extend({
    query : '',
    // this observer will fire:
    // 1.: every time a list object is added or removed from the underlying array
    // 2.: the query changes
    modelAndQueryObserver : function(){
            re = new RegExp( this.get("query"), 'i' );
        return this.get("model").forEach( function( list ){
            var cards = list.get( 'cards' ).forEach( function( card ){
            //the view has a class bound to the hide property of each card
            card.set( 'hide',
                    ( query.length ) ? !( re.test( card.get( 'description' ) ) ) : false
                );
            } );

            return list;
        });
    }.observes("model.@each", "query")
});

【讨论】:

  • 你可能忘记了这一行的右括号:re = new RegExp( this.get("query"), 'i' );
【解决方案2】:

这是一种实现,其中控制器的 modelcontent 属性在 处明显分开setupController 对应路由的钩子。

我在一个单独的文件中有不同颜色的球列表。

balls.js

[
    {"id":1,"color":"darkred"},
    {"id":2,"color":"lightred"},
    {"id":3,"color":"darkgreen"},
    {"id":4,"color":"lightgreen"},
    {"id":5,"color":"darkblue"},
    {"id":6,"color":"lightblue"}
]

App.js

App = Ember.Application.create();

App.Router.map(function() {
    this.resource('balls',{path:"/balls/:color"});
});
App.BallsRoute = Ember.Route.extend({
    model: function(params) {
        return params;  
    },

    serialize:function(model){return {color:model.color}},

    setupController:function(cont,model){
        var balls=Em.A();
        if(!App.Balls)
            App.Balls=$.getJSON("/start/js/balls.js");
        App.Balls.then(function(json){
            var re=new RegExp(model.color)
            balls.setObjects(json);
            var filtered =balls.filter(function(o,i){return re.test(o.color);});
            cont.set('content',filtered);
        });
  }
 });
App.ApplicationController=Em.Controller.extend({
    searches:[{color:"red"},{color:"blue"},{color:"green"}]
    });
App.BallsController=Em.ArrayController.extend({

});

HTML

<script type="text/x-handlebars">
    <h2>Welcome to Ember.js</h2>
    <nav>
        {{#each item in searches}}
            {{#link-to "balls" item}} {{item.color}} {{/link-to}}
        {{/each}}
    </nav>
    {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="balls">
    <ul>
    {{#each controller}}
      <li>{{color}}</li>
    {{/each}}
    </ul>
</script>

我没有使用 Ember 数据,因为我不喜欢它。

请检查此 Bin

【讨论】:

    【解决方案3】:

    关于异步调用的问题,您是对的。当您直接输入路线时,从您的 find 调用返回的对象是一个承诺,而不是一个实时列表。在处理它之前,您需要等待承诺解决。

    这样的事情应该可以工作:

    model : function( params ){
        var lists = App.List.find( ), //gets all the lists
            query = params.query, //query string from url
            re = new RegExp( query, 'i' );
    
        this.set( 'query', query );
    
        // 'lists' is a promise, once it has resolved we can deal with it
        lists.then(function(realLists){ // realLists is a real collection of models
          realLists.forEach(function(list){
             list.get('cards').forEach( function( card ){
               //the view has a class bound to the hide property of each card
                card.set( 'hide',
                    ( query.length ) ? !( re.test( card.get( 'description' ) ) ) : false
                );
             });
          });
        });
    
        // return the 'lists 'promise immediately
        // maybe before the 'lists.then' function above has run
        // Ember will resolve the promise for you and set up the controller correctly
        return lists;
    }
    

    请注意,当您调用上面的list.get('cards') 时,根据您的加载方法(侧加载与额外的 http 调用),您实际上可能会得到一个承诺,而不是一组卡片。如果是这种情况,您可以使用相同的技术。

    cards = list.get('cards');
    cards.then(function(realCards){
      realCards.forEach(...);
    });
    

    另外值得注意的是,在最新版本的 Ember Data 中,查找方法发生了变化。而不是App.List.find( ) 你会做this.store.find('list')。 (过渡指南有更多关于重大变化的信息。https://github.com/emberjs/data/blob/master/TRANSITION.md

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-09
      相关资源
      最近更新 更多