I have a Router set up which looks like this:
Social.Router.map(function() {
this.resource('accounts', function(){
this.resource('account', { path: ':account_id'});
});
});
When the account route is entered I'm using the activate method to do some DOM manipulation.
Social.AccountRoute = Ember.Route.extend({
activate: function(){
console.log('entered the account route')
}
});
This works great, the first time the account route is entered. Problem is that I have the ability to change from account to account without leaving the account route. Meaning that I can go from:
account/1
to
account/2
but the activate method only fires one time, the first time I enter the account route. Is there a method that will fire every time the account_id slug changes?
Not a very intuitive solution, but there are 3 methods that are executed every time the model changes:
serialize
setupController
renderTemplate
activate and deactivate are only triggered when entering a new route. However, the methods model, setupController, and serialize will be called with each change of /account/:account_id. (Edit: as pointed out by #mavilein in the comments, model will only be called when triggered by a URL change, and I left off renderTemplate).
Regardless, your routes and controllers shouldn't be concerned with DOM manipulation. Leave that to your views and templates by binding to significant values or observing changes in your controller(s).
Related
I'm making a simple web chat system with Ember.
I have a route /chatrooms that lists a few chatrooms, and then I also have /chatrooms/:chatroom_id that should show the actual chatroom with messages.
The second route is within the first one, like this:
this.resource('chatrooms', function() {
this.route('show', {
path: ':chatroom_id'
});
});
When I access /chatrooms, a call is made to the server (/api/chatrooms) is a list of rooms is returned and displayed, like expected.
When I click a room, the application transitions to /chatrooms/id, but no call is made to retrieve the messages (available at /api/chatrooms/id), even when I try to define a model.
I have a similar scenario with the users. A list of users is retrieved, then displayed. When a name is clicked, the profile is shown. No second call is made, but that's okay since Ember knows everything about the user already.
In my current case, when a list is first returned, it includes all the information except the messages. I believe that would be too much otherwise (10 chatrooms * 100 last messages = 1000 elements in my JSON for each request). So I want to call the server for messages only when a chatroom is selected.
Do you know how to do it, or maybe there's something wrong I'm doing in the first place?
Updates
Template code from app/templates/chatrooms.hbs
<h1>Chatrooms</h1>
<ul class="sub-menu nobullet flex mas">
{{#each chatroom in model}}
<li class="mrs">{{link-to chatroom.name "chatrooms.show" chatroom class="pat"}}</li>
{{/each}}
</ul>
{{outlet}}
In this case, model is an array of chatrooms.
My routes:
app/routes/chatrooms.js
export default Ember.Route.extend({
model: function() {
return this.store.find('chatroom');
}
});
app/routes/chatrooms/show.js
export default Ember.Route.extend({
model: function(params) {
return this.store.get('chatroom', params.chatroom_id);
},
actions: {
send: function() {
...
}
}
});
As discussed in this thread, when you link-to a route and the model is already loaded, the model hook of the route is not fired because there’s no need to reload the data.
If you transition to a route and all the context objects -the objects which will serve as models to templates- are passed in, the beforeModel and model hooks will not be called.
Later in the thread balint corrects:
In fact, the beforeModel hook still gets called in that case, it is only the model hook that does not.
If you want to force the model to be reloaded, you can change your link to use the ID instead of the model:
{{link-to chatroom.name "chatrooms.show" chatroom.id class="pat"}}
You could also load the data in the beforeModel or afterModel hooks, or setupController.
Also, in the chatrooms/show route, you are getting the already-loaded model from the Ember Data store rather than loading it from the server. Try this:
return this.store.find('chatroom', params.chatroom_id);
I ended up adding a links property to the JSON response for chatrooms. When the content of a chatroom has to be displayed, the link is used and the messages retrieved. It only requires two requests, and there's not need to preload all the messages from all the chatrooms and no need to make a request for each message.
I'm currently working on an Ember app and it is coming along fine but since I am new to MVC applications in general there are a lot of concepts that don't come naturally to me.
I am currently trying to return two models for my index route. I referred to another SO question (EmberJS: How to load multiple models on the same route?) for the correct method and it has worked great.
My problem is now that I need to only set one of the two models only if the user is authenticated. I am using ember-simple-auth, and currently this is what I've got:
// app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
if (this.get('session.isAuthenticated')) {
var _this = this;
this.get('store').find('user', this.get('session.uid')).then(function(user) {
_this.set('model.entries', user.get('entries'));
});
}
return Ember.RSVP.hash({
newEntry: this.get('store').createRecord('entry', {
body: 'Write here ...'
})
});
}
});
For some reason, this does not work. After my route is loaded, the model only has the 'newEntry' property and not an 'entries' property, although the promise does get fulfilled (I put console.logs inside to prove it).
What could be happening? And is this the best way to accomplish this?
There is a set of data that you always want to load, for every user. Do that in the model hook, that is actually the data for the route.
There is another piece of info that you want to add only if a condition is met (authentication). Do that in the afterModel hook.
...is provided the route's resolved model...
http://emberjs.com/api/classes/Ember.Route.html#method_afterModel
So, now you can append or remove data from the model. Or take any relevant action depending on the data that you received.
When you transition from index.html#/admin/authors/1/edit to index.html#/admin/authors/2/edit, deactivate on the route is not called.
When you transition from index.html#/admin/authors/1/edit to index.html#/admin/authors/new, deactivate on the route is called.
I would expect that in both cases deactivate is called ?
Is there another method at route level to detect that you switch from ../1/edit to ../2/edit ?
I need this in a master detail view (master is list with all authors, detail is form with textfields for each of the author properties). If the users selects another author in the list and the previous author was modified without saving, I need to rollback the transaction or ask the user 'do you want to save changes ?".
Deactivate is not called in this case since the edit route is still active. To detect switching from ../1/edit to ../2/edit use the route's willTransition event. For example:
App.FormRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
if(!this.controller.get('canNavigate')) {
alert('non-empty form!');
transition.abort();
}
}
}
});
See this gist for more examples: https://gist.github.com/machty/5647589
Is it possible to update a dynamic parameter in the URL for the current route without recreating the route's view?
Specifically, say I have a route that handles post/:post_id. The view for this route shows the post with the given id but (in an infinite-scroll style) also allows the user to smoothly scroll down to subsequent posts. I'd like to update the url to reflect the post they are currently viewing. If I call reaplceWith('post', newlyFocusedPost) the renderTemplate hook fires, my original view is destroyed, a new view rendered, and the smoothness is lost.
I'd love the equivalent of Backbone.js's:
router.navigate('posts/123', {navigate:false, replace:true});
Another solution would be if rendering the same template into the same outlet reused the existing view and just updated its bindings.
(My current solution is to use a subroute, postAtId, that handles the dynamic id with no view plus a call to controllerFor('post').set('content', newPost))
Re-rendering when only the context has changed is arguably a bug. There is a pull request that fixes this.
But it's also possible to work around this behavior in your own route by overriding renderTemplate so that it only renders immediately after entering the route for the first time. For example:
App.MyRoute = Ember.Route.extend({
activate: function() {
this.needsToRender = true;
},
renderTemplate: function() {
if (this.needsToRender){
this.needsToRender = false;
render();
}
}
});
I have an Ember application composed from 3 routes:
router.route('territory', { path: 'localhost/app/territory/:tid' });
router.route('aggregator', { path: localhost/app/territory/:tid:/aggregator/:aid' });
router.route(territory, { path: 'localhost/app/territory/:tid/aggregator/:aid/item/:iid' });
the possibles transition are from territory to aggregator, from aggregator to item, and from item to a sub item.
The sub item use the same route (the 3rd), just changing the iID value in the model of route.
I had created an action that allows the user to move into a particular route with some logic and at the end run the command:
model={
tid: "ttt"
aid: "aaa"
iid: "iii"
}
destination = 'item'; //the name of item route
controller.transitionToRoute(destination, model);
If I'm in the item route and I want to move to an other item, the URL will update, but not the content of the page. Obviously if I refresh the page with the generate URL the content will update.
Where is the problem? in the transition method that is Deprecated, or i have to use something different?
IMPORTANT: I'm using EmberJS - V1.0.0-RC.1
Is not a bug is just a normal situation in emberjs because every route have model and setupController.
The model function is used to retrive asynchronously from WS or Data module the necessary information (is a RSVP.Promise). When is complete the information will be passed to setupController function, where will be possible set properties of the controller connected with the view of current route.
Every time that i change the value of path but not the route, only setupController will be called.
To conclude, in my case, the problem was just an organisation problem of code.