ArrayController model records are hanging around between calls - ember.js

I have a search screen with search filters and a results screen with the table of search results.
this.resource('search', function() {
this.route('results');
});
When the user selects the submit button on the "search" route they are transitioned to "search.results" route.
When the user changes any of the filters while in the "search.results" route they are redirected to the "search" resource because the results are no longer valid.
The issue I had was that the previous ArrayController records were hanging around between searches. I had to manually clear the items in the array controller as below so that the old records would disappear.
Why do I need to do this. Is there a better way around it?
model : function() {
this.controllerFor('search/results').set('content', []); //why do I need this?
return this.fetchItems(1);
},

Controllers' content hangs around as you navigate within the app unless something changes them. To clear them, you could use
this.controllerFor('search/results').get('content').clear(). If you would rather not clear them and want to indicate that the results are being fetched in some way, like a loading spinner, look at ember's loading routes.

Related

Reloading a route's model with server json data and need ember-related opinion

I’m building a map with a search function. Basically, I’d like to store objects from the server within my ember app so that whenever I search for something that collection updates itself with the results from the server so the related view updates itself. It’s all on one page.
So far I have an Application Controller, and a Results ArrayController. Data is shown from the Results Controller. Now I’d need that when a search is requested, it gets JSON from the server and updates the results collection.
First question would be:
How would you build that?
I did a v1 with jQuery only and started a new one with Ember but I’m lost as of how structure-wise should I build it.
I built a small jsbin based on what I have here: http://emberjs.jsbin.com/IYuSIXE/1/
Second question:
How would I change a route's model content? Am I going in the wrong direction?
Thanks a lot
You can do both 1 and 2 with query params, check the documentation here https://github.com/alexspeller/website/blob/a96d9afe4506454b155cc64299e86e558ce3c9f1/source/guides/routing/query-params.md
When your route calls the model it will pass the query params, you can do your search against them
model:function( params, queryParams, transition ) { callToYourBackedEndWithQueryParams}
Second question: How would I change a route's model content? Am I
going in the wrong direction?
When the search is requested, in an action you can call this.transitionTo({queryParams: {sort: 'asc'}});, that will fire up again the model hook and you can do the query against your server again.
What I was looking for is a way to change the model on-the-fly.
So basically if I have this:
App.ResultsRoute = Ember.Route.extend({
model: function() {
// empty array that will contain results
return [];
}
});
I can do this to set the content of the model:
this.get('model').setObjects([{}, {}, {}]);
That way I can dynamically play with the models, load them with objects coming from almost anywhere.

Is there a "right" way to make the setupController hook fire consistently?

I have a page with a search bar. Upon entering text and clicking enter, a transition occurs to the same page with a query in the URL (ie .../search/banana). Due to the way the model and setupController hooks fire, I have set up my code as follows:
model: Updates the search text field with the text that was passed, /and changes the controller's model to the current JavaScript timestamp as a hack to make sure Ember calls setupController/.
setupController: obtains the text from the search field, and should then update the model with the proper search results.
What I'm doing in the model hook is a hack, but I'm not sure how else to do this in a way that remains consistent with my URL requirement (the search should work whether somebody manually enters an appropriate URL, or a transitionTo occurred)
I'd appreciate it if somebody could tell me if there's a "right" way to ensure that setupController is called regardless of whether or not Ember thinks that the model has changed (which seems to be the culprit that's currently necessitating the hack.)
I don't use setupController myself, but if the search bar is used on entire application, you should define this on ApplicationController. If not, you can define it in your controller.
Application template:
{{view Ember.TextField valueBinding="searchKeyword" action="doSearch"}}
App.js:
App.ApplicationController = Ember.Controller.extend({
searchKeyword: '',
actions: {
doSearch: function()
{
var keyword = this.get('searchKeyword');
// do your logic here
}
}
});
When user hit enter, it will trigger doSearch action and you can just
this.get('model').filter()
or any logic you want.
#dgbonomo, I think your existing approach is great. From an Ember point of view your model is changing each time that a new search happens. Think of the query as the main model, and the search results as a collection of models that "belong" to the query.

Transition from one route to another with a different model in Emberjs

I have a search page where we are getting different types of search results. In the list of search results I would like to use
{{#linkTo 'someResources.someResource' result}}{{result.Name}}{{/linkTo}}
And on the route someResources.someResource I want to use a totally different model than on the search page. How do I do that? When I click on the link for the linkTo it doesn't load the model again, instead it tries to use the model named result here.
So what I would like to do is to reload the model when I navigate to someResources.someResource based on the values in result.
The I do have a model named App.SomeResource and a find method for it that works if I go directly to that page.
Ember will bypass the model() hook when using linkTo as you've discovered. The assumption is that you passed a model to it, so it and will use that(result) as the model.
The next hook you can use is setupController. Since you have a model hook that works on the direct route, you can use call it directly from here.
One caveat is that you need to also allow for the direct route loading where the model will already have loaded.
setupController: function(controller, model) {
if (!model.isModel) {
this.model().then(function(result)) {
controller.set('model', result)
}
}
}
model.isModel is this check via an isModel property on the directly loaded model, which should be absent when passed with linkTo.
Note: the above code assumes that you are returning a Promise in your model() hook.
Since the problem is that I want a full reload of the model when doing the transition using linkTo won't work since that is using the model given to it. The solution to the problem is actually quite simple, just use a regular html a-tag instead. What I ended up doing was this:
<a {{bindAttr href="somePropertyInYourModel"}}>{{someTextProperty}}</a>
The property somePropertyInYourModel is a property containing the url to the new page. If the url is in the ember routes it will be as if you where typing that address in the address bar and pressing enter, but without the full reload of the page.
I think this is something that could be improved in ember, it would be much nicer if I could write something like:
{{#linkToRoute "resourceA.routeB" params="val1,val2,val3"}}Go here{{/linkToRoute}}
given I have this routes set up:
App.Router.map(function() {
this.resource("resourceA", {{path: "/resourceA"}}, function() {
this.route("routeB", {{path: "/:prop1/:prop2/:prop3");
}
});
I would like to get:
Go here
The order of the val1,val2,val3 matters, if the order is changed they should also be changed in the final url.

EmberJS Model find not up-to-date with underlying store

For a simple overview screen I have a developed a route that sets up a controller that does an App.Location.find().
App.LocationsIndexRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Location.find());
},
renderTemplate: function() {
this.render('locations.index',{into:'application'});
}
});
I naively assumed that this would simply go to the store and fetch me all the records, giving me an up-to-date view of the records.
Apparently not....
When an external process starts removing records from the database,
the App.Location.find() keeps on returning these deleted records.
(although the REST call doesn't show them anymore)
If an external
process starts adding records to the database, the
App.Location.find() picks them up.
If I delete the records form
within the Ember app itself the model is correctly updated.
How should I deal with this in my Ember app ? I'd like to have an up-to-date view on whatever is in my database. Right now I need to refresh the page (F5) to get an up to date view. Using the linkTo helpers shows me the stale data.
This seems to be yet another trivial thing that I completely missed in EmberJS. Is it somewhere mentioned in the docs why it behaves like that ? I guess there is a valid philosophy behind this behavior.
My overview screens is simply interested in showing the most up-to-date data. If a record is no longer in the DB the model should not return it anymore.
I've added a sample project in Github that is having this issues.
Try unloading all of the data from the store before you call find():
this.store.unloadAll('widget');
this.store.find('widget');
That will fully refresh your store to reflect what's on your server.
Have you tried App.Location.query() instead of App.Location.find()?

How to make a one page menu in Ember?

I'm wondering how can I make a menu like in this website with Ember ?
The page is split in different sections and we can scroll to go to each section, a click on the menu make the page scroll to the wanted section.
I'm not sure if I should have different routes in the router for this behavior, I would guess it's not the case as when we change route the view is remove from the DOM.
Then, how should I build the anchor link to each section ?
The best solution will automatically updates route when we scroll the page but any solution to handle the link and URL recognition will be fine.
Some may argue otherwise, but Ember may be a little overkill for a website landing page like you've shown. Ember is meant more for robust web apps that have multiple views and data they need to be connected with.
First off, if you look at their script, they're using jQuery to animate the body's scrollTop position to the respective div and setting window.location.hash to the hash of the menu element's href which also happens to be the ID of the <section/> the body scrolls to:
$(document).on('click', '#nav a, .clients-capabilities a', function(){
var target = $(this);
var hash = this.hash;
var destination = $(hash).offset().top;
stopAnimatedScroll();
$('#nav li').removeClass('on');
target.parent().addClass('on');
$('html, body').stop().animate({
scrollTop: destination
}, 400, function() { window.location.hash = hash; });
return false;
});
Secondly, they are not doing anything special to load to a specific position on page load. If you load any page on the web with a hash, the browser will look for an element with that ID and load at that position. For example, http://emberjs.com/#download.
Even if you still want to use Ember for this, you'd probably end up doing something similar with a single view loaded from the / route so I wouldn't even worry about Ember until your site becomes a full fledged web app.
Try using a *: catch all pattern in the router on the page you want to handle this scenario.
so lets say that index will work as a single page that you have to be able to automatically scroll to certain elements from the url.
Also you have posts and about sub pages that you can go to from the index page via links.
then...
App.Router.map(function() {
this.route('about');
this.route('posts');
this.route('index',{path:'*:'});
});
so if you have an element with id="elementToScrollTo" then the url /#elementToScrollTo would load the index page and scroll to that element.
See How to handle 'no route matched' in Ember.js and show 404 page? also for some other ways to solve this.
I hope this helps you.