Ember Data Each Observer

Hi,

I have a controller:

App.Page1Controller = Em.Controller.extend({
	application: Em.inject.controller(),

	saveWhenDirty: Em.observer('content.[].hasDirtyAttributes', function () {
		alert(this.get('locale'));
	}),
});

with an array of models like this:

App.Post = DS.Model.extend({
    locale: DS.attr('string')
});

and when I try to change the locale of the first object the observer does not fire:

App.__container__.lookup('controller:page1').set('content.firstObject.locale', 'en_US')

Can sb point me to the right direction?

Thanks

Can you give a little more context around what it is you’re trying to do? The Ember community has been moving away from observers lately in favor of “data down, actions up” so I might be able to offer some ideas with a little more context.

In fact, I think that in your example, what you want to do is to use the @each helper: Em.observer('content.@each.hasDirtyAttributes', function() {...}) …BUT, this has been deprecated, so this is definitely a good opportunity to explore some of the newer patterns for stuff like this.

I just want to autosave the property ‘locale’ of each object in the controller array.

It can be done with a timer or a change/blur event on every input in the template BUT how can you possibly beat the simple observer that automatically checks for isDirty?

<table class="table l-item-category-table">
	<tbody>
		{{#each browsenodes as |bnode|}}
		<tr>
			<td>
				{{input value=bnode.locale}}
			</td>
			<td>0 items</td>
		</tr>
		{{/each}}
	</tbody>
</table>

Ahh, gotcha. So, observers definitely feel like a nice solution at first — but because the observer will fire on every change, it starts to become a difficult to manage solution. There are some ways to handle this — i.e.: Ember.run.debounce — but, I think the data down / actions up pattern might better serve you. (I spent years fighting with Observers and am so glad that we have some better tools to not have to use them!)

In your case, that might look something like this:

// Controller
App.Page1Controller = Ember.Controller.extend({
  actions: {
    bnodeChanged(bnode) {
      bnode.save();
    }
  }
});
// Template
<table class="table l-item-category-table">
  <tbody>
    {{#each browse nodes as |bnode|}}
      <tr>
        <td>{{input value=bnode.locale on-blur=(action 'bnodeChanged' bnode)}}</td>
      </tr>
    {{/each}}
  </tbody>
</table>

Here, the value is still data-bound to the input — but, the action that triggers the particular bnode to be saved is only triggered when the blur event occurs on the node.

Hope this helps!

2 Likes

Brilliant!!

BUT, if you get in the “events” side of things you fall in the mud of per-browser implementation and if you throw tap events in the mix then it gets even worse. I always thought that this extra bit of abstraction that made Ember “frameworky” was killer for large apps because you could write tons of code with the MVC logic not worrying about safaris/ies etc…

Anyways +1 for your answer :smile:

Fortunately, these events are already an abstraction away from the browser events and are part of the framework…so it’s safe to assume that they’ll work across all supported browsers & devices (which is just about everything or > IE 8 if you’re on Ember 2.0).

http://emberjs.com/api/classes/Ember.TextSupport.html

2 Likes