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.
Related
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.
The documentation for emberjs clearly states that you should not use controllers, however sometimes you need to pass data into a component that is not the model for the corresponding route. For instance in an application I am working on I want to retrieve a list of records from the store and display them in a component so the user can select them as an attribute of the model for that route.
The advice I have received on this is to either create a controller and use it to retrieve the list in question or to add the list of records as an attribute of the model for that route, but since the former is inadvisable and the latter only makes sense if the item in question is a logical part of the model's schema (and therefore should probably be in there anyway) I am left feeling confused about how this apparently simple thing ought to be done. Can anyone help?
You can use Ember.RSVP.hash in your routes model hook. When the promise resolves, the results get passed as the second param in setupController.
// This would be in a route file like app/blogs/edit/route.js
model: function() {
return Ember.RSVP.hash({
blog: this.store.findRecord('blog', 1),
categories: this.store.findAll('category'),
});
},
setupController: function(controller, models) {
this._super(controller, models);
controller.set('model', models.blog);
controller.set('categories', models.categories);
},
OR
If you wanted all the data logic to exist in the component you can inject the data store service. This goes against the DDAU mantra (data down, actions up) but IMO it's a clean, modular solution. Useful if the extra content isn't visible immediately ie: components that open modal windows.
// This would live within the actual component
store: Ember.inject.service(),
loadCategories: function() {
this.get('store').findAll('category').then((categories) => {
this.set('categories', categories);
});
}.on('init'),
However, I would advise against this if the data (categories in this example) were immediately visible in the layout. Ember won't wait for these requests to complete before rendering so you would see blank spaces/whatever with the actual values loading in a half second later.
just be aware that components don't know anything about outside them self. The way I would solve the problem is by creating a bridge between controller and component by passing the property that you want to access to your component.
<script type="text/x-handlebars" data-template-name="sample-com">
{{sample-com
sampleRequests=sampleRequests
}}
</script>
App.MainController = Ember.Controller.extend({
//bridged properties that the controller must communicate between components/view
sampleRequests: 'hello world'
});
App.SampleComComponent = Ember.Component.extend({
sampleRequests: null
});
if there is a better way please feel free to suggest.
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.
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
Have just upgraded my application to 1.0.0-pre.4 and am in the process of changing my router to the new router API, however I cannot seem to be able to create a binding between my controllers anymore.
So in my main ApplicationController, I have the following:
App.ApplicationController = Em.ArrayController.extend({
user: App.User.create()
});
And then in v1 of the router API, I had the following:
App.IndexController = Ember.ArrayController.extend({
userBinding: 'App.router.applicationController.user',
});
However, with changing over to v1 of the router API, App.router is no longer defined. Everything I try does not seem to work, even setting userBinding to 'App.ApplicationController.user' does not work - it's as if the applicationController no longer is working.
What I am trying to achieve is to create an instance of my user model and then share it across a number of routes/views.
Any ideas would be appreciated.
Unfortunately Ember have hidden all instances of the singleton controllers to prevent users from implementing bad practice code. You shouldn't be referencing controllers explicitly, and instead you should be decoupling everything and using dependency injection to pass in things to your controller.
In the previous releases of Ember, we had connectControllers which allowed you to connect controllers to one another, but now with this latest release of Ember, we just use "set" in the router to pass in other controllers.
In your example you have a an IndexController and a UserController, to get access to the userController from within the indexController, you'll need to do something like the following:
(Bear in mind that all of this takes place in Ember's Router, which you can read more about here: http://emberjs.com/guides/routing/setting-up-a-controller/)
App.UserRoute = Ember.Route.extend({
setupController: function(controller) {
this.controllerFor('index').set('userController', controller);
}
});
Your indexController will now have the ability to read information from the userController. In a template this may look like the following:
{{controller.userController.name}}
There is another workaround using "needs" in ObjectControllers.
Here is a reference on how to use this.
http://eviltrout.com/2013/02/04/ember-pre-1-upgrade-notes.html