Is it possible to observe a model property after it was loaded? - ember.js

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')
});

Related

How to debug emberjs computed property

Let's say I have a computed property that depends on other properties. How can I find out which change on dependency property triggers re-compute on my property. Other than that, is there any way to debug computed chaining?
If your computed properties are in a component, you can try hooking into the didUpdateAttrs hook. didUpdateAttrs fire when values which were passed into the component update. Within the didUpdateAttrs you can check to see what attrs were changed by comparing the old/new values.
didUpdateAttrs: function(attrs) {
// attrs should have access to the old/new values
}
https://guides.emberjs.com/v2.6.0/components/the-component-lifecycle/
didUpdateAttrs runs when the attributes of a component have changed,
but not when the component is re-rendered, via component.rerender,
component.set, or changes in models or services used by the template.
A didUpdateAttrs is called prior to rerender, you can use this hook to
execute code when specific attributes are changed. This hook can be an
effective alternative to an observer, as it will run prior to a
re-render, but after an attribute has changed.
An example of this scenario in action is a profile editor component.
As you are editing one user, and the user attribute is changed, you
can use didUpdateAttrs to clear any error state that was built up from
editing the previous user.
Check that property is trigger by notifyPropertyChange() method. You can use notifyPropertyChange() method to trigger the computed property.
Maybe LOG_BINDINGS can show you what you need.
https://guides.emberjs.com/v2.14.0/configuring-ember/debugging/#toc_log-object-bindings
What I would do is set breakpoints in the computeds that are being depended on.

Using store.all adds items to controller

I'm using store.all to find all the current instances of an model in my store and then setting it as the model for the route in my controller like this:
model: function() {
this.store.all('activeShip');
}
That works fine, but I'm having some odd behavoir when I create a new model instance for that type. When I do, it's automatically added to the controllers model even though I never addObject. I've created a little demo here to see what I mean. As soon as you create a record, it's included in the model and updated in the view. I figured store.all would just run once, gather all the records and be done; not continue to update as new records are created.
Is this the desired behavior, a bug or am I doing something wrong? Thanks.
EDIT: Think these two issues my be related: #2375, #2542.
This is expected behavior, but maybe not desired in your situation. It's actually one common criticism of Ember Data--the inability to "fork" a model or RecordArray until the changes are persisted.
Secondly, a call to this.store.all returns a DS.RecordArray, which actually updates when new records are added to the store. If you don't want to show new records, consider using something like return this.store.all('post').filterProperty('isNew', false); to keep it from showing new records. Once they're persisted on the server, it will show in the array, but not before.
P.S. You're not actually "setting it as the model" unless you return this.store.all('activeShip'); (note return).

Determining which attributes were modified in an ember-data model

Is there any way of telling exactly which attributes were changed on a dirty record using ember-data?
The docs show a changedAttributes method, but I can’t use that directly because I want to bind it in a template.
I could set up observers for every field to get an event when they are updated, but that seems like a tedious solution, I was wondering if there was an easier way.
Edit: My use case is basically that I'm trying to indicate which fields were modified in an "edit" form.
There is an object _attributes on the record that has all the modified properties/values, you could use Em.keys on it for them all.

Ember Data Binding

Supposed I have a model containing 2 pre-populated fields. I would like to display these fields in the DOM for the user to modify if necessary. If I bind the DOM to the model using: {{input value=field1}} and {{input value=field2}}, then every time the user types in a character in one of these fields, Ember updates the bound model immediately. This is not the behavior I want. I prefer to show a button; when pressed, I want to validate data in the two fields in relation to each other, and if valid, update the model.
I considered creating mirrored fields in the controller and binding these to the DOM. Then create an action associated with the button to do the validation, and if data is found to be valid, copy data from the controller fields to the respective model fields. This technique may work, but seems like a very round-about way of doing something conceptually simple.
Do one of you Ember gurus out there have an opinion on how best to do this? I'm looking for best practices; please help.
I am not a guru nor am I familiar with client side validation in EmberData since our app uses server side validation.
Are you sure you can't allow your model to be updated immediately (i.e. bound to the template fields)?
If you did bind the model, then you could validate upon submission. If validation fails, you can just rollback the changes. If it passes, you can save the record. This is probably what I would do.
However, you clearly state that you do not want your model updated immediately, which means you don't want to bind your model.
Also, you have to do some processing that depends on multiple fields to validate, so it really sounds like binding to the controller and having a validation action is a good solution and that's what I would do if I wasn't binding to the model.

Force loading a single attribute with ember-data

I'm trying to find a way to update a single attribute of a model without it getting dirty. Just like Store.load but with just a single attribute.
The use case is that I want to upload an avatar image (which cannot be done with the current adapter's semantics AFAIK). I do it against a particular API endpoint and I afterwards I want to update the user with the resulting URL (so all his avatar appearances get updated).
I am in the context of a profile edition, so if I just load the whole user I loose all the changes of the form (because the values get overriden).
Is there any way to do so? Thanks!
You could model 'Avatar' separately, as a model in itself instead of an attribute. Then, that model can be updated via the store without worrying about conflicting changes with the rest of the User model.
I can think of two ways, but I haven't tried them. (They're both just workarounds)
You can set a user's property that is not a DS.attr. If it's a normal property, it shouldn't dirty the record.
If url is a database attribute, then create a computed property that listens to URL and use that in your templates.
App.User = Em.extend({
url: DS.attr('string'),
URL: function() {
return this.get('url');
}.property('url')
});
And then user.set('URL', imageUrl)
Another way could be to transition the record to the clean state.
user.get('stateManager').send('becameClean');
The second method is an ugly hack though.