Collection of objects of multiple models as the iterable content in a template in Ember.js

ぐ巨炮叔叔 提交于 2019-12-21 02:58:07

问题


I am trying to build a blog application with Ember. I have models for different types of post - article, bookmark, photo. I want to display a stream of the content created by the user for which I would need a collection of objects of all these models arranged in descending order of common attribute that they all have 'publishtime'. How to do this?

I tried something like

App.StreamRoute = Ember.Route.extend({
    model: function() {
        stream = App.Post.find();
        stream.addObjects(App.Bookmark.find());
        stream.addObjects(App.Photo.find());
        return stream;
    }
}

where the resource name is stream

But it doesn't work. I am using the latest released Ember 1.0.0 rc 2 and handlebars 1.0.0 rc 3 with jQuery 1.9.1 and ember-data.

Probably the way I am trying to achieve this whole thing is wrong. The problem is even if I am able to use the collection of objects of multiple models to iterate in the template, I would still need to distinguish between the type of each object to display its properties apart from the common property of 'publishtime'.


回答1:


You can use a computed property to combine the various arrays and then use Javascript's built in sorting to sort the combined result.

Combining the arrays and sorting them

computed property to combine the multiple arrays:

  stream: function() {
    var post = this.get('post'),
        bookmark = this.get('bookmark'),
        photo = this.get('photo');

    var stream = [];

    stream.pushObjects(post);
    stream.pushObjects(bookmark);
    stream.pushObjects(photo);
    return stream;
  }.property('post.@each', 'bookmark.@each', 'photo.@each'),

example of sorting the resulting computed property containing all items:

  //https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort
  streamSorted: function() {
    var streamCopy = this.get('stream').slice(); // copy so the original doesn't change when sorting
    return streamCopy.sort(function(a,b){
      return a.get('publishtime') - b.get('publishtime');
    });
  }.property('stream.@each.publishtime')
});

rendering items based on a property or their type

I know of two ways to do this:

  1. add a boolean property to each object and use a handlebars {{#if}} to check that property and render the correct view
  2. extend Ember.View and use a computed property to switch which template is rendered based on which type of object is being rendered (based on Select view template by model type/object value using Ember.js)

Method 1

JS:

App.Post = Ember.Object.extend({
  isPost: true
});

App.Bookmark = Ember.Object.extend({
  isBookmark: true
});

App.Photo = Ember.Object.extend({
  isPhoto: true
});

template:

<ul>
  {{#each item in controller.stream}}
      {{#if item.isPost}}
        <li>post: {{item.name}} {{item.publishtime}}</li>
      {{/if}}
      {{#if item.isBookmark}}
        <li>bookmark: {{item.name}} {{item.publishtime}}</li>
      {{/if}}
      {{#if item.isPhoto}}
        <li>photo: {{item.name}} {{item.publishtime}}</li>
      {{/if}}
  {{/each}}
   </ul>

Method 2

JS:

App.StreamItemView = Ember.View.extend({
  tagName: "li",
  templateName: function() {
    var content = this.get('content');
    if (content instanceof App.Post) {
      return "StreamItemPost";
    } else if (content instanceof App.Bookmark) {
      return "StreamItemBookmark";
    } else if (content instanceof App.Photo) {
      return "StreamItemPhoto";
    }
  }.property(),

  _templateChanged: function() {
        this.rerender();
    }.observes('templateName')
})

template:

<ul>
{{#each item in controller.streamSorted}}
    {{view App.StreamItemView contentBinding=item}}
{{/each}}
 </ul>

JSBin example - the unsorted list is rendered with method 1, and the sorted list is rendered with method 2




回答2:


It's a little complicated than that, but @twinturbo's example shows nicely how to aggregate separate models into a single array.

Code showing the aggregate array proxy:

App.AggregateArrayProxy = Ember.ArrayProxy.extend({
    init: function() {
        this.set('content', Ember.A());
        this.set('map', Ember.Map.create());
    },

    destroy: function() {
        this.get('map').forEach(function(array, proxy) {
            proxy.destroy();
        });

        this.super.apply(this, arguments);
    },

    add: function(array) {
        var aggregate = this;

        var proxy = Ember.ArrayProxy.create({
            content: array,
            contentArrayDidChange: function(array, idx, removedCount, addedCount) { 
                var addedObjects = array.slice(idx, idx + addedCount);

                addedObjects.forEach(function(item) {
                    aggregate.pushObject(item);
                });
            },
            contentArrayWillChange: function(array, idx, removedCount, addedCount) { 
                var removedObjects = array.slice(idx, idx + removedCount);

                removedObjects.forEach(function(item) {
                    aggregate.removeObject(item);
                });
            }
        });

        this.get('map').set(array, proxy);
    },

    remove: function(array) {
        var aggregate = this;

        array.forEach(function(item) {
            aggregate.removeObject(item);
        });

        this.get('map').remove(array);
    }
});


来源:https://stackoverflow.com/questions/15866899/collection-of-objects-of-multiple-models-as-the-iterable-content-in-a-template-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!