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

前端 未结 2 1179
萌比男神i
萌比男神i 2021-02-11 06:27

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 th

相关标签:
2条回答
  • 2021-02-11 07:01

    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);
        }
    });
    
    0 讨论(0)
  • 2021-02-11 07:02

    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

    0 讨论(0)
提交回复
热议问题