Ember.js filters data from a dynamic route

I am trying to filter data based on a search string using a dynamic route. When using the transitionToRoute function from the controller, the model data from the route is returned to viewproperly, however, when navigating directly to the url or refreshing the page, all forEach calls are not made, since the data length in the model is 0.

I have a feeling because the data loads asynchronously, but I'm not sure how to delay the forEach loop and rendering the view until the find promise is found and the forEach loop is completed.

Here is the model function of my router:

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

When a user accesses the application with a URL with the query string #/search/red , I want only cards with red color to be returned.

+4
source share
3 answers

I just opened this question, and this is my attempt to give an answer. As I mentioned in the comment, these are my main ideas:

with calculated properties:

  • Do not do filtering in the hook of the model, just return the lists.
  • Ask a request on the controller of your route (named SearchController?)
  • Do filtering in the controller as a computed property

with observers: (closer to the source code)

  • Do not do filtering in the hook of the model, just return the lists.
  • Ask a request on the controller of your route (named SearchController?)
  • Take the logic to hide the cards from your model hook and implement it as an observer on the controller

The best approach would be to use computed properties, but I was not sure how to do this (the Ember team claims that computed properties often lead to better code). Therefore, here is an example code sketch using the Observer approach . This should start normally:

Route:

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

Controller:

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

Here is one of the implementations in which the properties of the model and the content of the controller are clearly separated by the setupController hook of the corresponding route.

I have a list of balls with different colors in a separate file.

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> 

I do not use Ember data, as it is not convenient for me.

Pls check this bin

0
source

You are right that the problem is with asynchronous calling. When you enter a route directly, the object that is returned from your search is a promise, not a live list. You need to wait for the promise to resolve before dealing with it.

Something like this should work:

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

Note that depending on the loading method (side loading and optional HTTP call), when you call list.get('cards') above, you may actually receive a promise instead of a collection of cards. If in this case you can use the same technique.

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

It is also worth noting that in recent versions of Ember Data, the search method has changed. Instead of App.List.find( ) you will do this.store.find('list') . (The migration guide contains more information on violations. Https://github.com/emberjs/data/blob/master/TRANSITION.md )

0
source

Source: https://habr.com/ru/post/1499674/


All Articles