Model/SetUpController hook not always being called in ember when using TransitiontoRoute - ember.js

I am returning some static json from my ajax call for test purpose before the bakend is ready. Nut when I use transitionToRoute from some function I can see the model hook of the route is not always called. I guess it is caching the static json and I see the route rendering properly. But I am also setting some other properties of the controller in the setUpController hook which also doesn't get executed when the model hook is not called.
This variable needs to set whenever I am changing to this route. If setUpController is not the place to set it where should I set it . So it doesn't fail to get set when ember doesn't call model hook as part of caching process.
setupController : function(controller, model ) {
controller.set('isEditing',false);
controller.set('messages', model.messages);
controller.set('params', this.get('params'));
console.log('Set Up controller' );
},
model: function( routeParams) {
this.set('params',routeParams);
// return data omitted code
});
}
So the isEdiding field doesn't get set when model hook is bypassed. One get around solution is set it before transitioning like this
this.controllerFor("messages").set('isEditing',false);
// then do tranisitioning
Is there any better way to acheive the same thing? Like ideally where should this variable setting be done if done properly in Ember ?

Sorry I'm late and this might not be of any use for you. I just wanted to post it over here, if in case it might be of any use for others.
This link helped me, clear my problem.
Approach 1:
We could supply a model for the route. The model will be serialized into the URL using the serialize hook of the route:
var model = self.store.find( 'campaign', { fb_id: fb_id } );
self.transitionToRoute( 'campaign', model);
This will work fine for routing, but the URL might be tampered. For this case, we need to add extra logic to serialize the object passed into the new route and to correct the URL.
Approach 2: If a literal is passed (such as a number or a string), it will be treated as an identifier instead. In this case, the model hook of the route will be triggered:
self.transitionToRoute( 'campaign', fb_id);
This would invoke the model() and would correctly display the required URL on routing. setupController() will be invoked immediately after the model().
2nd one worked fine for me fine. Hope it's useful and answered the above question.

Related

How do you set variable in route and display in template?

I am looking to set a variable for each page that is set to text to display as a dynamic header sort of thing. How would I set a variable in a route and display it in the corresponding template? So far everything I have tried is not working.
The context of a template in a route is its controller, not its route. Try moving the action to the controller.
See updated twiddle: https://ember-twiddle.com/b454e10355ae8c708c3b8dc24b51e44e?openFiles=controllers.my-route.js%2C
For more information about controllers: https://guides.emberjs.com/v2.16.0/controllers/
You can only pass route variable to the template by using model hook. However, model is called only once. In order to update route variable and see its final value in the template, you need to wrap the variable. After that, you need to update the variable inside wrapped and the referance of the wrapped will not be changed. Your code will be like:
route.js:
model(){
this._super(...arguments);
this.set('myWrappedVariable', {myVariable: 1});
let modelResult = {myWrappedVariable: this.get('myWrappedVariable')};
return modelResult;
},
actions:{
increaseVariables(){
Ember.set(this.get('myWrappedVariable'), 'myVariable', this.get('myWrappedVariable.myVariable')+1);
}
}
template.hbs
myWrappedVariable: {{model.myWrappedVariable.myVariable}}
Take a look at this twiddle for this usage.
#Gaurav answer is totally right and should be considered as best practice. However if you have a good reason for setting a variable, which is not the model, in a Route, you could use setupController hook therefore:
import Route from '#ember/routing/route';
export default Route.extend({
setupController(controller, model) {
// Call _super for default behavior
this._super(controller, model);
// Set a variable on controller
controller.set('foo', 'bar');
}
});
As shown in the api docs you could also use setupController to set a variable on another controller by getting an instance of it using controllerFor method. However I would not consider this a good practice, cause if it's not well documented throughout the application, it's quite hard to maintain such code.
As shown by #Ahmet Emre Kılınç's answer you could also use a hash as model. If at least one of it's properties is a Promise, you should return a RSVP.hash(). Otherwise model hook would not block the transition until the Promise is fulfilled.

How to update an ember-infinity infinityModel?

I am trying to implement searching with ember-infinity. But, I do not understand the interaction between the route model and infinityModel.
I have the following code:
model() {
...
return this.infinityModel("myModel", {...}, {...})
}
My search action looks like the following:
search(searchCriteria){
const controller = this.get('controller');
...
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...}).then((myMod)=>{
...
controller.set('model', myModel);
});
}
So this works, but the my query gets fired twice when search is called.
The following only fires the query once.
search(searchCriteria){
const _this = this;
...
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...});
}
But my model does not update. However infinityModelUpdated() function is fired. So I assume that means the infiniteModel was updated, which I assume is my model.
I am pretty sure I am missing something simple. But any help would be greatly appreciated.
Just calling the following:
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...});
does not solve your problem; that is because that method call just returns fresh set of new objects retrieved; which is irrelevant with your original model that you had already returned from model hook. In other words; that method call makes the remote call but does not push the objects retrieved to the model that is already returned from the hook method. If you instead set the model of the controller; then of course the new data is updated to the screen; but I am not sure why a second remote call is being made. That might be related with existence of an infinity-loader already existing in your screen.
What I would suggest is to use updateInfinityModel provided instead of setting the model of the controller. Please take a look at the twiddle I have provided. It uses ember-cli-mirage to mock data returned by the server. Anyway, our point is looking at the makeInfinityRemoteCall action.
this.infinityModel("dummy", { perPage: 12, startingPage: 5}).then((myModel)=>this.updateInfinityModel(myModel));
Here a remote call is made upon button click and data is appended to the model already constructed in model hook. I hope this helps you clear things. Please do not hesitate to alter the twiddle yourself or ask further questions you have.
After your comment, I have updated the twiddle to change the model directly. The duplicate remote call that you have mentioned does not seem to be appearing. Are you sure an exact duplicate remote call is being made? Can it be just the case you are using infinity-loader at your template and a remote call for the next page is being made due to appearance within the view port?

EmberJS model hook: this.store.find returns no data. How do I redirect to a 404 page?

When my path /map/:id finds no value via this.store.find('location', route.id), I'd like to redirect to another page instead of receiving an "adapter's response did not have any data" error. It seems to stop processing before it even gets to the controller.
I thought the best way to do this was to extend DS.FixtureAdapter or to return a proxy object until this.store.find resolves. I read the documentation and it said to extend DS.FixtureAdapter via find or findMany hooks, among others. When I tried, none of the events seemed to fire, and I can't figure out an appropriate alternative return object. What am I doing wrong?
this.store.find() returns a promise. Promise resolution has 2 outcomes: 1. good and 2. bad. You can pass in 2 functions into the then() method to tell a promise what to do in each scenario.
So, let's say you are looking for a record and it's not there (bad outcome), you can tell ember to transition to another route.
App.DudeRoute = Ember.Route.extend({
model: function() {
var route = this;
return this.store.find('dude', 5).then(
function(dude){
return dude;
},
function(error){
route.transitionTo('nomansland');
});
}
});
Also note that you need to create a route variable, because just using this inside the bad scenario won't work, since this gets a new context.
Working example here

Emberjs - How to set model of different route when using named outlets

I'm loading data from XboxLive and other services in my ApplicationRoute and want to display that information using named outlets. I used this StackOverflow answer as a guide, but it's not working.
Fiddle is here: http://jsfiddle.net/sandalsoft/7xHfp/
In the ApplicationRoute, the model hook loads data from online services. This data is accessible as model.xbox, model.facebook, etc... setupController then sets the content of the XboxprofileController to model.xbox.
This doesn't work when I call App.Xboxprofile.find() (returns a promise), but it does work when I call App.Xboxprofile.findSimple() (returns a simple object). Why doesn't this work when find() has any kind of asynchrony? Am I missing something simple, or is this not the right way to architect this?
thanks
Thanks to #alexspeller in #emberjs for the answer.
I mistakenly thought model.xbox was a promise. It's an object. When I changed my model hook to use RSVP.hash() it worked perfectly:
model: function(controller)
var allModels = Em.RSVP.hash({
xbox: App.Xboxprofile.find('major nelson'),
}).then(function(hash) {
return Em.RSVP.hash(hash);
});
return allModels;
},

Ember.js: ensure parent is loaded

This might be a simple question, but I can't seem to find any current (RC1) example on how to achive this.
So let's say I've got the following two routes:
App.PostRoute = Ember.Route.extend({
});
App.PostCommentRoute = Ember.Route.extend({
});
How do I ensure that the controller of the PostRoute is loaded when I access the PostComment route directly. I.e. by calling #/post/comment directly from the browser?
You need the PostRoute model hook to return a promise. If you are using ember-data, this is done automatically for you.
The router checks, and if the object returned by the model hook implements the then function (which means it's a promise), it will transition the router into a loading state until the promise is resolved (which means the data was fetched). Then it will continue to the PostCommentRoute.