I dont understand how ember loads related models.
Lets say thats my model:
export default DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
states: DS.hasMany('state', {async: true})
})
I load this on of my outer routes. When navigating though an ember-app (into nested routes), model-contexts are often provided for routes not by the model-hook of the route but with the link-to helper (when using dynamic-segments, the model-hook will be ignored). When the target route has something in its template like {{#each model.states as |state|}}, ember will load automatically the related model-entrys from (in that case)the state-model. (How and why? - Just because of the each in the template?
When accesing an dynamic-route directly, the model is not given and the model-hook of the dynamic route will be called. So loading my model is easy: just override the model hook and load record with the url parameter (return this.store.find('item', {title: params.item_title})). But no related models will be loaded. How can I do that manually and what (and when) is the way ember do it by default?
How does Ember know to automatically fetch relationships?
ember-data allows you to define relationships (currently only belongsTo and hasMany) with async option set to true or false. Based on this option, after fetching the model from the API (via find method), ember-data will expect relationships object either directly in the response JSON or not. You have async: true (which is rather a common and supported way of handling relationships), therefore ember-data assumes that in your JSON response it gets the ids of states, but not necesseraily the states themselves.
If you define your hasMany as async: true, it always returns promise. It means, that if you make something like this:
this.get("item").get("states")[0]
Will not work, as get("states") will not return an array, but the promise to fetch this array. However, Handlebars are smart (as the get and set methods of Ember) and it can find out what is a promise and wait for it to resolve before using it content. Therefore if your template contains:
{{#each model.states as |state|}}
Handlebars finds out that states is promise, wait for it to resolve, and after resolve they use its content as an array. Very similar behaviour can be found using belongsTo method. Assuming that your item has one state, if you use the code as follows:
this.get("item.state.somePropertyOfState")
Even if you did not fetched the state and currently you don't know what is the somePropertyOfState value, ember get will find out it's a promise and fetch it for you automatically.
How can I manually fetch relationships?
There are couple of ways to do it.
First one is to explicitly fetch them in ember code:
this.get("item.states").then(function(states) {
# now you have fetched the states that the item has, and moreover
# they are accessible in states variable
});
Second, you can let Ember do it automatically for you as I described formerly (e.g. via template).
Third, you can send the relationships with your response using a mechanism called sideload. This will significantly reduce the number of API requests. When you allow ember fetch your relationships, ember is performing one request per one relationship object, which means that if you have ten states that belongs to item, the API will be hit ten times. However, if you sidelod the states while fetching the item, the request will be sent only once. Take a look here to get more info about that.
Sorry for a long post, but I hope that I clarifed a bit.
Related
I'm fetching a list of records, say posts, without relationships. When I pick a single post, I would like to see the post immediately, and background-load its relationships.
This is possible like so:
this.store.findRecord('posts', params.id, {
reload: false,
backgroundReload: true,
Once the relationships are loaded, the view is automatically updated.
However, some relationships are complex and take some time to load. They become relevant after scrolling. There is one hasMany relationship that is important for the initial pageview. And their relationships are also important, but I prefer to defer them too.
So, is it possible to do the following points in order when clicking on a single post?
Open post directly from cache (reload: false)
Background-reload including one relationship excluding the relationship's relationships
Background-reload relationship including relationship's relationships
Background-reload including all relationships
E.g. after the initial template is drawn using the cached model, 3 more updates will happen.
You could use reload() method that is available on DS.Model and DS.ManyArray. It returns a Promise that settles after the record has been reloaded. You could use that Promise to chain your reloading as needed.
You should guard against reloading a not yet loaded record by using isLoaded property available on DS.Model as well as on DS.ManyArray.
You could use the eachRelationship method of DS.Model to iterate over all relationships.
I am new to ember and am trying to migrate an existing application, and would like to know what the recommendation is for modeling a single object that will be reused in multiple components on every page. ie: As part of the initial load, I would like to perform a GET request against a URL like 'https://example.com/currentUser' and get an object back like:
{
name: "Users Name"
email: "user#email.com",
profileImg: "http://example.com/pictureOfUser.png"
... snip ...
}
This object will then be used in components for menus, toolbars, and a form for updating it via a post to the same URL.
What is the best way to model this workflow in ember? Given that this is an incidental object and not the focus of most routes, it does not seem to make sense to specify it as the model of them. Also, does ember data handle cases where a model is a singleton and does not have an ID, or would I need to use something like Ember.$.ajax ?
What do you mean by "where a model is a singleton"?
If you use the ember-data default adapter, then yes, a model needs to have an ID, it's part of the JSONAPI spec. If you already have a backend with different conventions, take a look at extending or swapping out the default adapter.
A service is a singleton, and there is nothing preventing you from making an AJAX call there. You would be losing out on all the nice things that come along with ember-data, but, you can do it.
Update
Here's a bin of my problem: http://emberjs.jsbin.com/ruwet/8/edit?html,js,console,output4
I have a simple filtered property on an array controller:
import Ember from 'ember';
export default Ember.ArrayController.extend({
activeEncodes: function() {
return this.filterBy('model', 'isActive');
}.property('model.#each.isActive');
});
I'm connected to a websocket and am updating encodes in my store with
store.store.push('encode', data);
Even though it's just updating a certain property on my data (progress), activeEncodes is being recalculated. This is causing some visual jank in my templates.
If I use model.#each.id, it doesn't recalculate, but any other property seems to trigger the CP to recalculate (even if that property is not changing).
Any idea why this is happening?
When you push it causes the entire model to invalidate. This is then considered a new model which fires any observers watching anything on that model (since it sees it as a new model). It's tangentially related to Ember data model reload causes item in {{each}} to be removed/inserted back - losing current state as well.
id is likely a special case, I believe it lives outside of the scope of the underlying properties on the model (same reason you don't define the id on the model). I'm just guessing on that though.
I'm trying to save a record (using ember-data) with a belongsTo relationship to itself
App.Account = DS.Model.extend({
name: DS.attr(),
parent: DS.belongsTo(App.Account)
});
When loading the record from the server, the relationship is loaded correctly. However when I try to save the record (create or update), the belongsTo record isn't updated. I'm trying to set it explicitly:
a.set('parent', b); (where a and b are both valid App.Account records)
having a look at the data sent over the wire, the belongsTo attribute isn't sent. What am I doing wrong? am I able to set the belongsTo relationship without specifying a hasMany relationship?
In Ember Data Beta 2 you will need to change DS.belongsTo(App.Account) to DS.belongsTo('account').
As far as I've been able to tell, Ember doesn't assume that the parent key is stored on the child in a belongsTo situation, instead it assumes that the parent keeps a list of all children. (Yeah, it's weird...) I've been able to work around it by also setting up parentId : DS.attr('number') and then manually populating the parentId. Less than ideal, but it does work.
Not so much an answer as more info on the problem. I'm fighting a similar battle, and what I've found in my case is that when serialization happens the parent property is a DS.PromiseObject--this in itself wouldn't be a problem, but at the time serialization is happening it isn't isSettled…therefore, the Ember Data serialization process picks up undefined. The property is settled before and after the save, so I'm thinking that the fact that the parent object is being saved also and is refreshing its relationships (or something) is causing the child's parent property to become unsettled, leading to the problem.
Another part of this puzzle is that the Ember Data serializers actually serialize to a Javascript object, so you internally have an object like this: { "parent": undefined }, but when that object gets serialized to a string (likely by the browser's toJSON implementation?) the parent property gets dropped entirely--and that's what you see go across the wire. And I'm guessing that's why #Jeremy is observing what he's observing.
Now I just have to figure out how to fix it. Anyone???
Assume you have the following routes in an Ember application.
App.Router.map(function() {
this.resource('series', function() {
this.resource('serie', { path: '/:serie_id' }, function() {
this.resource('seasons', function() {
this.resource('season', { path: '/:season_id' }, function() {
this.resource('episodes', function() {
this.resource('episode', { path: '/:episode_id' });
})
});
});
});
});
});
How would I link to a specific episode using the linkTo helper that Handlebars provides? In other words, how does Ember figure out what the other parameters of the URL should be, that is, the serie_id and episode_id? The documentation states that I should pass an episode model to the episode route as shown below.
{{#linkTo "episode" episode}}
This is to link to the following URL structure.
/series/:serie_id/seasons/:season_id/episodes/:episode_id/
When I use the linkTo helper like that, Ember throws an error telling me that it cannot call get with id on undefined. I assume that it uses the episode model to figure out what the serie_id and episode_id are and my guess is that the model needs to conform to a specific convention (structure or blueprint) for Ember to find these ids.
These are the aspects that I find most difficult about Ember. It isn't very transparent even if you use Ember in debug mode. Any pointers or references are much appreciated.
UPDATE 1: After some digging, I found out that the route's serialize method is a key element in accomplishing this. However, when I use the linkTo helper as illustrated above, the model passed to the route's serialize method is undefined for some reason (even though it is not when passed to the linkTo helper. The question that led to this discovery can be found here.
UPDATE 2: It turns out that the serieSeason route's serialize method receives the wrong model, an episode instead of a season, when the link is generated. It isn't clear, though, why it is receiving the wrong model. Where does the model parameter of the serialize method come from?
UPDATE 3: The linkTo helper works fine if I return static data from the serialize method of each route involved, which means that the linkTo helper isn't involved in the problem.
It turns out that the answer could be found in the properly documented source of Ember ... because that is what one does after searching the web for several days.
The answer is simple. The linkTo helper accepts more than one model. For each dynamic segment of the destination URL, you pass a corresponding model. Each passed model will become the model of the corresponding route in the destination URL. In the example that I describe above, this results in the following.
{{#linkTo "episode" serie season episode}}
The serie model will be passed to the serie route, the season model to the season route, and the episode model to the episode route. What confuses many developers is that the route's model hook isn't triggered when you use the linkTo helper. This isn't too surprising if you realize that the developer provides (or can provide) the model for the corresponding route by passing one or more models (or zero).
Because there isn't much documentation for deeply nested resources, it wasn't trivial to find out how the linkTo helper does its job under the hood. Diving in Ember's source definitely helps getting up to speed with the framework.