Ember 1.13.2 ArrayController replacement

Hi.

I’m trying to find a replacement for ArrayController.

I’m using this:

Ember.Controller.extend(Ember.SortableMixin, ...

… and its mostly working except the rendered array (in the template) doesn’t update when I add or remove items from the model (array). If I delete a model, it doesn’t update the template until I force it to re-render (by navigating away and back, for example).

Is there another mixin perhaps, that will give me the functionality that was being provided by ArrayController?

The model is being provided by the route’s model hook… using store.filter The template is a typical master-detail list, using ArrayController and ObjectController.

I’m fine with replacing ObjectController with a Controller and using components for everything (except the top/route level).

Should I be using a component for this?.. If so, how would I replace a Route + ArrayController + Template with a component?

1 Like

What does your template look like?

If you have something like

{{#each model as |item|}}
  <li>{{item.name}} <a {{action 'remove' item}}>x</a></li>
{{/each}}

and then, in your controller:

actions: {
  remove: function(item) {
    this.get('model').removeObject(item);
  }
}

there should be no additional work needed. Maybe you forgot to use “removeObject”? You have to use the special Ember methods on arrays, the same way you use set / get everywhere, otherwise the bindings won’t be updated.

My template:

{{#each filteredContent as |item|}}
    {{list-item model=item}}
{{/each}}

I’m using this in my controller.

filteredContent: function () {
  var content = this.get('arrangedContent'),
      search = this.get('search');

  if (search) {
      content = content.filter(function (item) {
          return item.get('name').toLowerCase().indexOf(search.toLowerCase()) !== -1;
      });
   }
   Ember.Logger.log('filteredContent', content);
   return content;
}.property('arrangedContent.@each', 'arrangedContent.@each.name', 'search')

Not sure about the list-item thing, but I guess the problem here is the property('arrangedContent.@each', ...) part. I don’t think that’s actually doing anything. The correct syntax for observing changes to an array content is property('arrangedContent.[]', ...).

I could be wrong though.

property('arrangedContent.[] didn’t make any difference. The @each syntax works in every other place I use it… and works in this case when I use ArrayController

It works when I use Ember.ArrayController.extend( instead of Ember.Controller.extend(Ember.SortableMixin. (using either .@each, or .[]).

It seems that arrangedContent is not being updated when the model changes (when using Ember.Controller)

Now I understand what you’re trying to do. The way I understand the Ember deprecations is that there is no drop-in replacement for ArrayController. If you still want to use it, go for it. But the deprecation is not just syntactic sugar - you would implement the concept differently. For example, you wouldn’t have an “arrangedContent” property anymore.

In your case you could simply have:

filteredContent: function() {
  ...
).property('model.[]', 'model.@each.name', 'search')

but it probably depends on your exact use case.

The thing is I need it to be arranged according to the sortProperty (functionality provided by Ember.SortableMixin) - which works ok, except the arrangedContent doesn’t seem to update when the model is updated…

The arrangedContent is updated and works correctly when navigating to the page/or changing the queryParams. But If I add new items, remove items, or change the queried parameter for an item (so the item should be filtered out), the template doesn’t update.

If I use property('model.@each' instead of property('filteredContent.@each' then it works, except the items are not sorted according to the sortProperty (need to use arrangedContent for that to work).

I’m not familiar with the SortableMixin and whether it’s deprecated or not, but:

sortedContent: Ember.computed('model.[]', 'model.@each.name', function() {
  return this.get('model').sortBy('name') // or whatever
}

That should do the same thing as your previous arragendContent.

Thanks, I will try that later… looks promising

That works… thanks very much… its not a perfect solution, but it does work.

Do you know if it is possible to sort descending using sortBy()?.. any way to toggle ascending or descending?

I have found another solution. I have copied the arrangedContent function from the Ember.SortableMixin and modified it, only slightly… All I did was to change this

arrangedContent: computed('content', 'sortProperties.@each', {

to this (adding the .[] to the content)

arrangedContent: computed('content.[]', 'sortProperties.@each', {

I didn’t change the function itself. and now everything works for me.

Apparently not, but you can of course always use Javascript’s Array#sort with any custom function.

Sounds like a common use case (one that used to be covered by ArrayController moreover), so it would be nice if some utility methods could be provided by Ember.

Ember.SortableMixin does that.

Now I’ve found a fix… My drop-in replacement for ArrayController is to do this

export default Ember.Controller.extend(Ember.SortableMixin, ArrangedContentFix, ...

and the ArrangedContentFix is this:

export default Ember.Mixin.create({
    arrangedContent: Ember.computed('content.[]', 'sortProperties.@each', {
        get(key) {
            return this._super(key);
        }
    })
});

the ‘fix’ is the .[] bit. The original is like this

arrangedContent: computed('content', 'sortProperties.@each', {

Now it works exactly as it did when using ArrayController.

1 Like

Interesting. Still feels like kind of a hack, so maybe Ember.SortableMixin should be rewritten?

I smell a PR. Thanks for tackling/figuring this out this @LozJackson and @Fryie. Will save me some brain space.

agreed, it does feel like a hack, and yes (in my opinion) the Ember.SortableMixin should be fixed.

If someone wants to do a PR, that would be great… at the moment I don’t have time… too busy working on my app.

Why not use Ember.computed.sort?

that looks like it would work… I will give it a try later. (I’m happy with my current solution, but this could be a good alternative replacement)

That’s nice, but arguably it might still be handy to support a more descriptive, higher-level abstraction similar to what Ember.SortableMixin did.

Well the thing is, everything is supposed to be a component now, and what makes sense in a component is an explicit transformation to a model, and that is exactly what Ember.computed.sort provides.

What do you mean by “an explicit transformation to a model”?

I don’t see a contradiction to “everything is a component” here. Just a useful abstraction for any kind of composite property that can be sorted according to some internal key. Kind of similar to Array#sortBy.