Javascript Ember.js从动态路由筛选数据

Javascript Ember.js从动态路由筛选数据,javascript,ember.js,Javascript,Ember.js,我正在尝试使用动态路由根据搜索字符串筛选数据。当从控制器使用TransitionRoute功能时,路由中的模型数据会正确返回到视图,但是当直接导航到url或刷新页面时,所有forEach调用都不会执行,因为模型中的数据长度为0 我有一种感觉,这是因为数据是异步加载的,但我不确定如何延迟forEach循环和视图的呈现,直到find的承诺得到解决并且forEach循环完成 以下是我的路由器的型号功能: model : function( params ){ var lists = App.L

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

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

以下是我的路由器的
型号
功能:

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的应用程序时,我只希望返回其中包含“red”的卡片

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

具有计算属性:

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")
});
  • 不要在模型钩子中进行过滤,只返回列表
  • 在路由的控制器(名为SearchController?)上设置查询
  • 在控制器中按计算属性进行筛选
有观察员:(更接近原始代码)

  • 不要在模型钩子中进行过滤,只返回列表
  • 在路由的控制器(名为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")
});
控制器:

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")
});

这里是一个实现,其中控制器的模型内容属性在相应路由的设置控制器钩子处明确分开

我在一个单独的文件中列出了不同颜色的球

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 = 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({

});
App.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 = 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.js
{{{#搜索中的每个项目}
{{{#链接到“balls”项目}{{item.color}{{{/链接到}}
{{/每个}}
{{outlet}}
    {{{#每个控制器}}
  • {{color}}
  • {{/每个}}
我没有使用余烬数据,因为我对它感到不舒服


请检查此

关于异步调用导致的问题,您是对的。当您直接输入路由时,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(...);
});

另外值得注意的是,在最新版本的余烬数据中,查找方法发生了变化。您可以使用
this.store.find('List')
,而不是
App.List.find()
。(过渡指南中有更多关于中断更改的信息。)

是的,这肯定是因为异步调用。你可以做出自己的承诺。但是这个代码看起来有点难看。我建议在保存列表的控制器上设置搜索查询。然后,此控制器可以使用计算属性筛选列表。您的方法是非常必要的,而Ember希望促进声明式编程。@mavilein谢谢,我是沿着这条路开始的,但是在将查询参数传递到控制器时遇到了问题。我很想看到你的评论的一个例子作为答案(如果还有什么我可以投赞成票的话),为什么不直接将参数传递给
find()
方法,让后端来做这项工作(当然,除非你使用的是fixture数据)?你可能忘记了这一行的右括号:
re=newregexp(this.get(“query”),'I');