Ember observer fires on array controller when it shouldn't - ember.js

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.

Related

ember - recommended way to fetch a collection of objects to use as menu options without them being considered part of the model

I have a model Foo which has a field barId which has a belongsTo relationship to a model named Bar. I want to have a menu of Bars to set the barId value in my "new" Foo template.
I've already created a select component which displays things properly.
Where is the appropriate place for me to do a JSON fetch of the Bar collection? I'm assuming it should be stored in the controller as that decorates the model and makes extra context available to the template.
Doing an RSVP.hash() in the router's model() doesn't seem appropriate to me, as it would consider the Bar collection part part of the Foo model. Should I be using the router's afterModel()/setupController(), the controller's init() or set this up somewhere else?
Sorry I don't have a jsFiddle or anything, I don't know how to set one up now that my code requires babel.
Any help would be appreciated.
it turned out to be a non-issue as using Ember.RSVP.hash() in my router's model() without using setupController() as it defaults to model.foo, model.bar, etc, which you can access in controllers/templates/etc.
What I was originally worried about was that the other collections/objects I fetched from the store would be stored along with my 'model' when I did a
model.save(), but instead I'd be using model.foo.save(),

Ember.js: Loading related models directly

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.

Is it possible to observe a model property after it was loaded?

In my use case I want to invalidate a model when some of its properties change so that the user needs to revalidate before publishing his changes
invalidate: function () {
this.set('model.valid', false);
}.observes('model.solution', 'model.setup', 'model.tests')
the problem (I think) is the observer fires whenever the model changes, including when it is loaded, which may be a time where the model is valid and all attributes didn't change, but since it fires valid is set to false.
using isDirty was not helpful as the model is then always dirty
Incase my intent isn't obvious, what I have is a model that I want to be invalidated whenever some properties change, saving changes while invalid causes to model to be unpublished, it also required that the model be valid in order to publish it (however not to save it).
currently my workaround is to just validate when publishing but I would prefer if I can do the former.
I'm having the same issue, but I've found some workaround that works in my case.
Please see the link below. There is an example of using "observesBefore" function instead of "observes"
How can an observer find out the before and after values of the observed property in Ember.js?
I use observesBefore with arguments to check previous state of the model and the new attribute value being set to find out whether this is the first time the model loaded or changes made by the user.
Yes, tricky situation. Any time a model switches all of those properties have changed. In that case, I'd define invalidate on the model itself, that way the only time they change is when that particular model changes.
App.Foo = DS.Model.extend({
...
valid: true,
invalidate: function () {
this.set('valid', false);
}.observes('solution', 'setup', 'tests')
});

Lazy loading belongsTo

I have a child and a parent model that are linked via DS.belongsTo. In my child/index template I link to the parent:
{{linkTo "parent" parent}} go to parent {{/linkTo}}
Now here's my issue: Ember Data sideloads all of the parent models (even though I specified async: true in the belongsTo relationship) one by one. This seems to be caused by linkTo because when I remove that line this behavior stops.
How do I prevent ED beta 3 from doing this? The use case is that my child model is a partial model of the parent so this behavior defeats the point of my setup (and seems unnecessary).
While #kinpin2k's answer would work, I found a better one: I just discovered that {{link-to}} accepts ids as arguments, too.
In my case I changed parent: DS.belongsTo('parent') to parent_id: DS.attr('string'). There was no need to change any of my back end since that's how Active Model Serializer returns it anyways. I then change my template to:
{{link-to "parent" parent_id}} go to parent {{/link-to}}
and everything works nicely because (as #kingpin2k nicely explained) Ember does not access the parent model anymore.
EDIT: Updated to reflect new syntax: {{linkTo}} is now {{link-to}}
It's isn't Ember Data, it's Ember accessing the model. Async means don't load it until someone tries to access it, using it in a link-to is considered using it.
You could add it to an action, and then transition to the route instead of using the link0to

Saving belongsTo (without hasmany)

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???