I can't seem to get the model from inside the controller, even though the controller seems to have a model property set. The following:
export default Ember.ObjectController.extend({
init: function() {
this._super();
console.log(this.get('model'));
console.log(this.model);
console.log(this);
}
}
prints out:
Any ideas?
So it turns out that when I examine the model by setting a break point it is empty. I assume the console shows content because it updates the model once the content arrives.
In init() the model is unreachable:
init: function() {
this._super();
console.log(this.get('model')); // null
}
Same for any method .on('init'):
onInit: function() {
console.log(this.get('model')); // null
}.on('init'),
But the model is accessible to actions (I'm assuming because the model has been set up by the time the action is called):
someAction: function() {
console.log(this.get('model')); // model object as expected
}
So to answer my question, this.get('model') can be used to access the model from the controller, but just not in init() or .on('init') methods.
The an Ember.ObjectController is a proxy for the model. So the model can be referenced using this as you found in your example. So then in a template {{this.aModelAttr}} or just {{aModelAttr}}. Like you question suggests it's a bit confusing.
The ObjectController is being deprecated as of Ember 1.11.0. So to simplify use Ember.Controller and in your controller you can reference the model by this.get('model')
Then in the template use {{model.aModelAttr}}
To give the model a domain specific ie: books or user name use
export default Ember.Controller.extend({
domainSpecificName: Ember.computed.alias('model')
}
Then in your templates you can use {{domainSpecificName.aModelAttr}}
Related
In my Ember application, I wanted to have a controller wrapping a collection of models, that I could inject into other controllers.
I've set it up like this:
app/controllers/zones.js:
export default Ember.Controller.extend({
model: function () {
return this.store.find('zone');
}
});
app/controllers/zones/index.js:
export default Ember.Controller.extend({
needs: ['zones'],
zones: Ember.computed.alias('controllers.zones.model')
});
This seems like it ought to work, but unfortunately, it doesn't. I get this error in my JavaScript console (in the browser):
Error: Assertion Failed: The value that #each loops over must be an Array. You passed function () {
"use strict";
return this.store.find('zone');
}
I've tried moving stuff around, or using ArrayController rather than just Controller, but I still get this error.
This makes very little sense to me, any ideas?
Here is the thing, model is the function need to resolve the model for route not controller. That model then automatically injected to controllers model property.
Ember way
In ember way I would suggest move this model definition to the route for controller. Something like this.
export default Ember.Route.extend({
model: function (param) {
return this.store.find('zone');
}
});
This is the Ember way of doing thing. Resolve model in route then have controller to filter / decorate it.
I would also suggest using ArrayController instead of Controller since you are handling number of models.
The other way
Again if you want to have model resolved in controller. I warn you its not the Ember way but you can do it something like this -
export default Ember.ArrayController.extend({
//dont override the model property
mydata: function () {
return this.store.find('zone');
}.property('model'),
});
I figured out the problem – I just needed to set my overridden model implementation to be a property, like this:
app/controllers/zones.js (injected controller):
export default Ember.Controller.extend({
model: function () {
return this.store.find('zone');
}.property() // `.property()` turns the function into an iterable object for use in templates and the like.
});
The main controller is still the same.
app/controllers/zones/index.js (active route controller):
export default Ember.Controller.extend({
needs: ['zones'],
zones: Ember.computed.alias('controllers.zones.model')
});
I have a problem getting the models inside a controller of a route accessed using {{link-to}}
From my understanding (after reading http://emberjs.com/guides/routing/asynchronous-routing/) the model hook of a route doesn't get called when the route is accessed from a {{link-to 'route' model}}. The model is passed directly to the controller. This is a way that Ember ensures that no AJAX called will be made unnecessarily.
For example if I go to {{link-to 'post-review' post}} and I need to pass more than a post model to the PostReviewController.
App.Router.map(function () {
...
this.resource('post-review' , {path: '/post-review/:id'});
...
});
PostReviewRoute = Ember.Route.extend({
//method doesn't get called
model: function(params){
return Em.RSVP.hash({
post: this.store.find('post', params.id),
reviewTypes: this.store.find('reviewType')
});
}
});
The ReviewTypeis a model which has no relationship with Post so that I can access it directly using post.reviewType. A post has several reviews. A review has a reviewType. But I must show all the reviewTypes inside of a combobox.
Anyways, the model hook doesn't get called and I cannot access the this.get('reviewTypes') from PostReviewController. I understand that the beforeModel or afterModel hooks are used for this purpose: to pass additional models to a controller when the route it's accessed from a link-to and not directly from the browser URL. The documentation doesn't show how you can do that! Please enlighten me if you know how!
Thanks!
setupController to the rescue, in your route use the setupController hook to set the reviewTypes like so:
PostReviewRoute = Ember.Route.extend({
setupController: function(controller, model){
var postId = model.get('id');
this._super.apply(this, arguments);
this.store.find('reviewType').then((records)=> {
controller.set('reviewTypes', records);
});
this.store.find('post', postId).then(....)
},
// your code
});
You should now be able to call this.get('reviewTypes').
Let me know if this does it for you.
I am using ember-cli and there is one controller which gets using a render helper and hence no route. Example
{{render 'ref-type' ref-type}}
Now inside the controller ref-type
export default Ember.Controller.extend({
actions{
isShown: function() {
var m = this.get('model'); //here model is undefined can i know why?
}
}
});
and model ref-type is
export default Ember.Object.extend({
getData: function(){
return 'xyz'; //data is returned hre
}
});
why am i not able to access model in the controller.
Adding a raw JSBin example
JSBIN
Should the model always be DS.Model.extend? i do not think so.
Also Instead of ref-type i have used 'sample' as the name, so that it is easier to understand
You never initialize your model. According to your JSBin example you must have property named sample in your TodosController. ember will not create an object by itself. I have edited your JSBin. It might not be the best approach but I tried to explain what is going on.
If you put log {{log sample}} just above your render helper you will notice that your sample property is already undefined.
In Ember if I have a model that is a list of users, if in the UsersController I do:
users = this.get('model');
users.map(function(user) {
user.name
});
should it not resolve the promise and return the user records? I'm confused on why this is not working for me, or how to get the model data the correct way. Thank you in advance.
The model promise is resolved by the router. Ember, by default, sets the controller's content property as the route's model unless you override the route's setupController() method. Your issue lies in the formatting of the map function.
It seems like you're using an array controller, because the model is an array, so do the following:
App.UsersController = Em.ArrayController.extend({
users: function() {
return this.map(function(user) {
return user.get('name');
});
}.property('#each.user'),
});
You can make this code even more streamlined by using Em.Array's mapBy() method, as follows:
App.UsersController = Em.ArrayController.extend({
users: function() {
return this.mapBy('name');
}.property('#each.user'),
});
If you're using the list of users in your template you can do this easily with a {{#each users}} helper. However, if you're using this list for other properties in the controller, be sure to to use the right observer to watch for items being added to the array:
someOtherProperty: function() {
var users = this.get('users');
// Do stuff with users array here...
}.observes('users.[]')
See setting up a route's model and setting up a controller if you're unfamiliar with how the models stuff works.
Working hard on my Ember app here, and it's going along fine. However, I've run into an issue of unexpected behaviour and I'm not sure regarding the best approach to this problem.
The problem is that in a specific route, I want to render another route into another outlet. However, the other route that I render into the other outlet doesn't retain it's own model.
If I do this:
App.TestRoute = Ember.Route.extend({
model: function() {
return {
heading: "Test",
testContent: "This is test."
}
}
});
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render("test", {
outlet: "left"
});
this.render({
outlet: "right"
});
},
model: function() {
return {
heading: "Index",
indexContent: "This is index."
}
}
});
... and access the IndexRoute, I would expect the TestRoute's model to be rendered into the TestRoute's template, but only the IndexRoute's model is relayed to both templates.
Fiddle here:
http://jsfiddle.net/3TtGD/1/
How do I allow Ember to use the default model for a route without having to expressively merge them? It seems tedious.
Also, having the same name of some model properties, like {{heading}} is desirable, but not necessary.
What's the best approach for solving this issue?
Thank you for your time.
Best regards,
dimhoLt
In the renderTemplate method you're telling Ember to render a template inside an outlet but it will just default the controller to the one managing the route. Given it's the controller handling the route it makes sense that it manages all the templates within that route.
Of course you can specify a different controller using:
this.render("test", {
outlet: "left",
controller: 'test'
});
it can in turn be a controller you already instantiated (and maybe set its content):
var testController = this.controllerFor('test');
testController.set(....)
this.render("test", {
outlet: "left",
controller: testController
});
About using the model: You can call this.modelFor('test') inside the route and it will return the model of the test route (it even knows if it has already been resolved). I usually do this when I need to access the model of one of the parent routes.
I believe it makes sense to access the model of a parent route, but not so much if you're accessing the model of an unrelated route. Why don't you want to merge both models?