0
votes

I have created a collection passing a collection view and a collection. The collection references a model I have created. when fetching the collection the items get rendered succesfully, but when the models change, the itemViews are not being re-rendered as expected.

for example, the itemAdded function in the tweetCollectionView is called twice ( two models are added on fetch ) but even though the parse function returns different properties over time for those models ( I assume this would call either a change event on the collection, or especially a change event on the model, which I have tried to catch in the ItemView ) the itemChanged is never called, and the itemViews are never re-rendered, which i would expect to be done on catching the itemViews model change events.

The code is as follows below:

    function TweetModule(){

            String.prototype.parseHashtag = function() {
                return this.replace(/[#]+[A-Za-z0-9-_]+/g, function(t) {
                    var tag = t;
                    return "<span class='hashtag-highlight'>"+tag+"</span>";
                });
            };

            String.prototype.removeLinks = function() {

                var urlexp = new RegExp( '(http|ftp|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:/~+#-]*[\w@?^=%&amp;/~+#-])?' );

                return this.replace( urlexp, function(u) {
                    var url = u;
                    return "";
                });

            };

            var TweetModel = Backbone.Model.extend({
                idAttribute: 'id',
                parse: function( model ){
                    var tweet = {},
                        info = model.data;

                    tweet.id = info.status.id;
                    tweet.text = info.status.text.parseHashtag().removeLinks();
                    tweet.name = info.name;
                    tweet.image = info.image_url;
                    tweet.update_time_full = info.status.created_at;
                    tweet.update_time = moment( tweet.update_time_full ).fromNow();

                    return tweet;
                }
            });

            var TweetCollection = Backbone.Collection.extend({
                model: TweetModel,
                url: function () {
                    return '/tweets/game/1'
                }
            });

            var TweetView = Backbone.Marionette.ItemView.extend({

                template: _.template( require('./templates/tweet-view.html') ),

                modelEvents:{
                    "change":"tweetChanged"
                },
                tweetChanged: function(){
                    this.render();
                }
            })


            var TweetCollectionView = Marionette.CompositeView.extend({
                template: _.template(require('./templates/module-twitter-feed-view.html')),
                itemView: TweetView,
                itemViewContainer: '#tweet-feed',
                collection: new TweetCollection([], {}),
                collectionEvents: {
                    "add": "itemAdded",
                    "change": "itemChanged"
                },
                itemAdded: function(){
                    console.log('Item Added');
                },
                itemChanged: function(){
                    console.log("Changed Item!");
                }
            });

            this.startInterval = function(){
                this.fetchCollection();
                this.interval = setInterval( this.fetchCollection, 5000 );
            }.bind(this);

            this.fetchCollection = function(){
                this.view.collection.fetch();
                this.view.render();
            }.bind(this);

            //build module here
            this.view = new TweetCollectionView();
            this.startInterval();

    };

I may be making assumptions as to Marionette handles event bubbling, but according to the docs, I have not seen anything that would point to this.

2

2 Answers

0
votes

Inside your CollectionView, do

this.collection.trigger ('reset') 

after model have been added.

This will trigger onRender () method in ItemView to re-render.

0
votes

I know I'm answering an old question but since it has a decent number of views I thought I'd answer it correctly. The other answer doesn't address the problem with the code and its solution (triggering a reset) will force all the children to re-render which is neither required nor desired.

The problem with OP's code is that change is not a collection event which is why the itemChanged method is never called. The correct event to listen for is update, which according to the Backbone.js catalog of events is a

...single event triggered after any number of models have been added or removed from a collection.

The question doesn't state the version of Marionette being used but going back to at least version 2.0.0 CollectionView will intelligently re-render on collection add, remove, and reset events. From CollectionView: Automatic Rendering

When the collection for the view is "reset", the view will call render on itself and re-render the entire collection.

When a model is added to the collection, the collection view will render that one model in to the collection of child views.

When a model is removed from a collection (or destroyed / deleted), the collection view will destroy and remove that model's child view

The behavior is the same in v3.