There is a Question here Ember.js accessing model values from afterModel
but it doesn't exlpain how to access the value in the afterModel hook. Can someone advise?
my model:
model: function () {
return Ember.RSVP.hash({
accounts: this.store.find('account').then(function (account) {
})
});
},
Did you read the documentation? The model is the first parameter passed to afterModel.
https://api.emberjs.com/ember/3.13/classes/Route/methods/transitionTo?anchor=afterModel
On a slightly unrelated note, I wonder why you are returning Ember.RSVP.hash from the model hook. That makes the model (after being resolved) a hash containing a property accounts, which is presumably what you are interested in. That means every time you access the model you have to say model.accounts. Why not just return this.find(... itself, which would make the model itself the array of account instances which you could refer to directly as model?
Related
Is it possible to access route model inside route action?
I am passing multiple objects inside a route model to template,
model: function() {
return {
employeeList : this.store.findAll("employee"),
employee : Ember.Object.create()
}
}
From the route action I am want to modify the route model.employee. I tried the following, but I am not getting the object.
actions:{
editAction : function(id) {
var emp = this.get("model");
console.log(emp.employee);
}
}
Can anyone give a solution to get and modify model object(employee)?
First problem is that you should return a promise from the model hook. That way, the transition will wait for the promise to Resolve. return { /*...*/}; returns an object and not a promise, even if the object itself contains promises.
The solution is to use Ember.RSVP.hash like:
model() {
return Ember.RSVP.hash({
employeeList: this.store.findAll('employee'),
employee: Ember.Object.create()
});
}
This will return a promise that resolves when all inner promises resolve.
The second problem is that you can't use this.get('model') in a route. If you think about it the model property is the hook itself and not the resolved model. Solutions:
That action is sent from the controller/template. Can't you pass the model as a parameter? That way you could access the model through the function arguments.
If you absolutely need to, this.modelFor(this.routeName); returns the model for the current route.
You can access the model in route through the controller like this.controller.get('model').
You could also implement the setupController hook and access there the model. You can then do things like this.set('employeeModel', model); for later access.
this.get('context')
gives you access to the model in the route action.
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.
I am relatively new to Ember.js, so I am giving myself a project to figure things out.
I believe I understand the very basics. Controllers contain state-logic, while models contain model attribute-logic.
In my example, I have a collection of models. These models contain an attribute that represents an id of another model:
App.Pokeball = DS.Model.extend({
name: DS.attr('string'),
rate: DS.attr('number'),
pokemon: DS.belongsTo('pokemon')
});
I have a Controller that contains selectedPokemonId and selectedPokemon attributes. When selectedPokemonId changes, I want to automatically update all the Pokeball models.
I know its awful, but here is the function I am using to update the Models:
selectedPokemon: function(selectedPokemonId) {
var pokemonId = this.get('selectedPokemonId'),
store = this.store,
id = 1,
max = App.Pokeball.FIXTURES.length;
for (id,max; id<= max;id++) {
store.update('pokeball', {
id: id,
pokemon: pokemonId
});
}
return store.find('pokemon', this.get('selectedPokemonId'));
}.property('selectedPokemonId'),
Technically, this does what I need it to... but I am certain I am not doing this the "ember way", there has to be a cleaner way to bind the relationship between controller state and models.
Github Example Code here
Working example
I like to work directly with models as objects instead of managing record ids. Doing this greatly simplifies your code. Here's how I would accomplish this.
First, your route should return all the models you want to work with using the model hook.
The route's model hook should look something like:
model: function()
{
return Ember.RSVP.hash ({
pokeballs: this.store.find('pokeball'),
pokemon: this.store.find('pokemon')
});
}
In general you want to do store.find calls in the route model hook because they can be asynchronous (return a Promise) and the model hooks waits for promises to resolve before proceeding. This ensures your data will always be ready for your controller to work with it. More here: http://emberjs.com/guides/models/finding-records/. Note that the model we'll be working with is an object with two properties, pokeballs and pokemon, which are both collections representing all the respective objects in the store.
In your controller, instead of a selectedPokemonId, you can reference a selectedPokemon model object directly. You can then observe the change to the selectedPokemon using 'observes' and then simply set the selectedPokemon on each pokeball and save each pokeball model to persist it back to the store. If you're just using fixtures you could get away without even saving each pokeball because 'set'-ing a property on the model object is enough to change it in the store.
selectedPokemonObserver: function()
{
var thePokemonToSet = this.get('selectedPokemon');
this.get('pokeballs').forEach( function( aPokeball ) { // note you can also do this.get('model.pokeballs') since the model is an object with two properties, pokeballs and pokemon
aPokeball.set('pokemon', thePokemonToSet); //note that instead of an id, i'm setting the pokemon model object here to satisfy the belongsTo relationship
aPokeball.save(); // you might not need this if using only fixtures and not persisting to db.
});
}.observes('selectedPokemon')
Anything referencing these model objects in your templates will automatically be updated.
I think the "Ember way" to do what you want to accomplish is to use an observer instead of a property:
...
selectedPokemonObserver: function() {
var pokemonId = this.get('selectedPokemonId'),
store = this.store,
id = 1,
max = App.Pokeball.FIXTURES.length;
for (id, max; id <= max; id++) {
store.update('pokeball', {
id: id,
pokemon: pokemonId
});
}
}.observes('selectedPokemonId'),
selectedPokemon: function() {
return this.store.find('pokemon', selectedPokemonId);
}.property('selectedPokemonId'),
...
Let's imagine that I have Articles resource and Article one with dynamic segment.
ArticlesRoute and ArticleRoute load Article using the same underlying data. But ArticlesRoute uses basic data using different serializer - the point is to not load all articles, relations, and other not essential data in index.
Using e.g link pointing to /articles/5 (from index) transition to ArticleRoute with full data. model for link-to helper uses explicit id to trigger model hook:
link-to 'article', this.id
The model is fired but what in store is, is only basic data. I'd like to invoke reload (to load full data) only if I'm transitioning from ArticlesRoute - I want to have LoadingRoute.
If I call model.reload() in afterModal there's a delay before template changes (no loading route)
I don't know what the state of ember-data partially loaded records is. As I recall it was on some wish list. I have had success doing it like so.
Instead of calling reload or find I do a query. In order to make the route model hook fire I always pass a primitive to the route (like you are doing in link-to).
here is what my model hook looks like.
model: function(params) {
var query;
query = {
id: params.id,
includes: ['users'],
fields: ['name', 'date', 'user.name', 'user.whatever']
};
return this.store.find('article', query).then(
function(results) {
return results.get('firstObject')
}
);
}
Hope this helps.
According to the docs:
If you're using Ember Data, you only need to override the model hook
if you need to return a model different from the record with the
provided ID
But this does not work for me, ember data gives me wrong data.
App.UsersEditRoute = Ember.Route.extend
model: (params) ->
return ['Just', 'Some', 'Random']
setupController: (controller, model) ->
controller.set('model', model) # Returns #get('store').find 'user', params.user_id
This should return ['Just', 'Some', Random], but instead it gives me the original #get('store').find 'user', params.user_id
Why and how do I get the data I want?
Btw, If I do like below, everything works, but I want to know why my model function never is called.
setupController: (controller, model) ->
controller.set('model', ['Just', 'Some', 'Random']) # returns ['Just', 'Some', 'Random']
Thank you, I'm using ember-data 0.14 and ember 1.0.0
The model hook, for routes with a dynamic segment, is only called when the page is (re)loaded, here's what the ember guide says (the note at the end):
Note: A route with a dynamic segment will only have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper), then a model context is already provided and the hook is not executed. Routes without dynamic segments will always execute the model hook.
I had a similar problem when I wanted to override the model hook. The answer from Simon gave me the right direction. In addition, it should be noted, also from the ember guide but in the Links section, that the {{link-to}} helper takes:
At most one model for each dynamic segment. By default, Ember.js will
replace each segment with the value of the corresponding object's id
property. If there is no model to pass to the helper, you can provide
an explicit identifier value instead. The value will be filled into
the dynamic segment of the route, and will make sure that the model
hook is triggered.
So the bottom line is that by replacing the model in the {{link-to}} helper (in my case 'product') by the object id (in my case 'product.id'), my model hook is now called every time.