Martin Maillard
Martin Maillard

Reputation: 2811

Merge two model arrays in a route

I have a controller handling a list of models. These models are of two different types (e.g. Message and Comment). In order to use an ArrayController I would have to merge both lists into one. Is there a way to do this ?

Class-based polymorphism, as proposed in this thread, would solve my problem, but they are not likely to be implemented soon.

In my current solution, I use an ObjectController reveiving both comments and messages. I then merge them using a computed property:

App.SomeRoute = Ember.Route.extend({
  model: function (params) {
    return Em.Object.create({
      comments: this.store.find('comment'),
      messages: this.store.find('message'),
    });
  },
});

App.SomeIndexController = Ember.ObjectController.extend({
  merged: Em.computed.union('messages', 'comments'),
});

It works, but I don't benefit from all the niceties of an ArrayController (like sortProperties for example).

What I would like to do is something like:

App.SomeRoute = Ember.Route.extend({
  model: function (params) {
    var comments = this.store.find('comment');
    var messages = this.store.find('message');
    return merge(comments, messages);
  },
});

where merge returns something similar to what is returned by this.store.find('model').

Upvotes: 3

Views: 3553

Answers (3)

andrewohyeah
andrewohyeah

Reputation: 56

This might help. I stumbled upon this a while back and your question reminded me

https://gist.github.com/sebastianseilund/6096696

Upvotes: 0

Martin Maillard
Martin Maillard

Reputation: 2811

Inspired by @bmeyers' answer, and after exploring ember-data's source a little bit, I came up with a solution that is reusable and not too terrible. It is probably not optimal, but it does the work.

App.Store = DS.Store.extend({
  findMultiple: function (types) {
    var self = this;
    var recordsByType = types.map(function (type) {
      return self.find(type);
    });
    return self.mergeArrayPromises(recordsByType);
  },

  mergeArrayPromises: function (promises) {
    var promise = Ember.RSVP.all(promises).then(function(arrays) {
      var mergedArray = Ember.A();
      arrays.forEach(function (records) {
        mergedArray.pushObjects(records.toArray());
      });
      return mergedArray;
    });

    return DS.PromiseArray.create({
      promise: promise,
    });
  },
});


App.SomeRoute = Ember.Route.extend({
  model: function (params) {
    return this.store.findMultiple(['comment', 'message']);
  },
});

Upvotes: 1

bmeyers
bmeyers

Reputation: 754

I asked a similar question recently, here is how I solved the issue.

App.SomeIndexController = Ember.ObjectController.extend({
  sortProperties: ['some field'],
  sortAscending: false, // false for descending
  merged: function() {
    var comments = this.get('comment') || [], // This gets wherever you've stored the comments array
        messages = this.get('message') || [];// This gets wherever you've stored the messages array
    var stream = Ember.A();

    stream.pushObjects(comments.toArray());
    stream.pushObjects(messages.toArray());

    return Em.ArrayProxy.createWithMixins(Ember.SortableMixin, {
        content: stream,
        sortProperties: this.sortProperties,
        sortAscending: this.sortAscending
    });
  }.property('messages.@each', 'comments.@each')
});

Hope this works for you as well. Just an FYI, for my example, my controller is actually one that is rendered, so I do not set up the model for it in the route. I simply have properties on my controller, lets say, commments and messages that constantly updated themselves as RecordArrays.

So for your example you may need to observe .property('model.messages.@each', 'model.comments.@each')

Upvotes: 7

Related Questions