0
votes

I'm bumping my head to properly listen to remove event on a collection to re-render a view.

My view looks like this:

define(function(require) {
    var templ     = require('text!./../../templates/delete-deal-templ.html'),
        utils     = require('utils'),
        dealName  = '' ;


    return Backbone.View.extend({

        events: { 
             'click #delete-deal-btn' : 'deleteDealHandler',
        },

        template: _.template(templ),

        initialize: function() {
            this.listenTo(this.collection, 'remove', this.render);
        },

        render: function() {
            $(this.el).html(this.template({deals: this.collection}));
            ...
            return this;
        },

        deleteDealHandler: function(e) {
            e.preventDefault();
            ...
        }

    });
}); 

And gets properly instantiated with the collection like this:

DealEngine.getDeleteDealView = function() {
    require(['models/deal-model','views/delete-deal-view'], function(DealModel, DeleteDealView) {
        var DealCollection = Backbone.Collection.extend({
                model: DealModel,
                url: '/alldealsofbusiness/' + DealEngine.businessID
            }),
            dealCollection = new DealCollection(),   
            promise = dealCollection.fetch();
        promise.done(function(collection) {
            new DeleteDealView({collection: collection}).render();
            DealEngine.router.navigate('deletedeal');
            window.scrollTo(0, DealEngine.top_pos);
        }); 
        promise.fail(function() {
            var utils = require('utils');
            utils.growl('Your deals', 'could not be retrieved from our cloud, please try again!', 'error');
        });
    });
};

The template:

<div id="view-header" class="align-center">
    <h2>Delete a Deal</h2>
    <h5>Please note: Deals are automatically deleted after they have expired</h5>
</div>
<div class="row">
    <div class="col-xs-1 col-md-2"></div>
    <div class="col-xs-10 col-md-8">
        <div class="form-style" >
            <div id="select-deal" class="form-group dropdown">
                <a id="deal-select" class="btn btn-outline-inverse btn-lg dropdown-toggle" data-toggle="dropdown" role="button">
                    Select Deal
                    <span class="caret"></span>
                </a>
                <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
                    <% _.each(deals, function(deal) { %>
                        <li><a class="deal-option"><%= deal.deal.name %></a></li>
                    <% }); %>
                </ul>
            </div>
            <div class="col-xs-12 col-md-12">
                <div class="row align-center">
                    <div class="form-group">
                        <button id="delete-deal-btn" type="button" class="btn btn-outline-inverse btn-lg">Delete</button>
                    </div> 
                </div>
            </div>
        </div>
    </div>
    <div class="col-xs-1 col-md-2"></div>
</div>

The this.collection object has the right data, this.render is a function, however, the views initialize method throws

Uncaught TypeError: undefined is not a function

Obviously, I'm doing something wrong and would appreciate if someone could shed some light on it.

2

2 Answers

0
votes

One thing I noticed is that you are setting a variable equal to the require call which I believe is async. So the javascript will continue to process even if that variable hasn't been set.. you might want to try something like:

    var templ = require('text!./../../templates/delete-deal-templ.html', function(templ){
//process logic in here
});

Or you can define the variables as dependencies in the define call of the module.

I've rewritten it into something similar and this works for me (you will have to modify it to comply with what you are trying to do). Note that when you are getting an attribute from a model you want to use deal.get('name') instead of deal.name (unless that property is set directly on the object of course).

    <script type="text/template" id="testTemplate">
        <div id="view-header" class="align-center">
            <h2>Delete a Deal</h2>
            <h5>Please note: Deals are automatically deleted after they have expired</h5>
        </div>
        <div class="row">
            <div class="col-xs-1 col-md-2"></div>
            <div class="col-xs-10 col-md-8">
                <div class="form-style">
                    <div id="select-deal" class="form-group dropdown">
                        <a id="deal-select" class="btn btn-outline-inverse btn-lg dropdown-toggle"
                           data-toggle="dropdown" role="button">
                            Select Deal
                            <span class="caret"></span>
                        </a>
                        <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
                            <% deals.each(function(deal) { %>
                            <li><a class="deal-option"><%= deal.get('name') %></a></li>
                            <% }); %>
                        </ul>
                    </div>
                    <div class="col-xs-12 col-md-12">
                        <div class="row align-center">
                            <div class="form-group">
                                <button id="delete-deal-btn" type="button" class="btn btn-outline-inverse btn-lg">
                                    Delete
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="col-xs-1 col-md-2"></div>
        </div>
    </script>

    <script type="text/javascript">
        (function (_, Backbone, $) {
$(document).ready(function () {
            var templateHtml = $('script#testTemplate').html();
            var templ = _.template(templateHtml);

            var view = Backbone.View.extend({
                template: templ,
                render: function () {
                    this.$el.html(this.template({deals: this.collection}));
                    return this;
                },
                initialize: function () {
                    this.collection.on('remove', this.render, this);
                }
            });

            var testModel = Backbone.Model.extend({
                defaults: {
                    name: 'test2'
                }
            });

            var dealCollection = new Backbone.Collection();
            dealCollection.add(new testModel());

            dealCollection.add(new testModel());

            var deleteDealView = new view({
                collection: dealCollection
            });

            var html = deleteDealView.render().$el.html();
            $('#test').append(html);
});
        })(_, Backbone, jQuery);
    </script>

Btw, I would look into Marionette, it can handle most of the boilerplate rendering logic for you. https://github.com/marionettejs/backbone.marionette/tree/master/docs

0
votes

Well, the root of my issue was passing the server response to the view and not the collection. This is the correct code snippet:

var DealCollection = Backbone.Collection.extend({
            model: DealModel,
            url: '/alldealsofbusiness/' + DealEngine.businessID
        }),
        dealCollection = new DealCollection(),   
        promise = dealCollection.fetch();
    promise.done(function() {
        new DeleteDealView({collection: dealCollection}).render();
        DealEngine.router.navigate('deletedeal');
        window.scrollTo(0, DealEngine.top_pos);
    });