1

I am working on a phonegap app using backbone.js. I have the following models:

  1. A Measure
  2. A Measures Collection (basis of the main view)
  3. A Code
  4. A Codes collection (child of Measure)

I then have the following Views

  1. Main Page
  2. List of Measures
  3. Measure page
  4. Code List Page (codedOptionsPage) (includes a header and then a div that is the container for the list)
  5. Code List collection view (codedItemListView, manages the list in the above page)
  6. Code Item (codedItemView - one for each list item)

I want the Measure object to be the model for all Views 3 - 6 so that, when anything changes, I can update the main object and save it.

What is happening in the code below is that, upon first render for the codedOptionsPage, the "this.model" is an instance of the Measure model. However, on subsequent calls to render triggered by adding a code to the Measure's code collection, "this.model" is a reference to the prototype, not an instance, so I get an error saying "Object function (){ return parent.apply(this, arguments); } has no method 'toJSON'"

I am pretty sure the code below is not what it should be, as I'm still struggling through this approach, and I'm happy to take general advice, but I'm particularly wondering what is happening to the model.

directory.views.codedOptionsPage = Backbone.View.extend({
    events : {
        "click a.add-item" :"add"
    },
    initialize: function () {
        this.template = _.template(directory.utils.templateLoader.get('coded-options-page'));
        this.model.attributes.codes.bind('add', this.render);
        _.bindAll(this, "add");
    },
    add: function() {
        var code = new directory.models.code();
        this.model.attributes.codes.add(code);
    },
    render: function(eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        this.listView = new directory.views.codedItemListView({el: $('ul.codes', this.el), model: this.model});
        this.listView.render();
        return this;
    }
});

directory.views.codedItemListView = Backbone.View.extend({
    el: 'ul.code-list',
    initialize: function() {
        this.model.bind("reset", this.render, this);
    },
    render: function(eventName) {
        $(this.el).empty();
        _.each(this.model.attributes.codes, function(code) {
            var li = new directory.views.codedItemView({model: code}).render().el;
            $("#code-list").append(li);
        }, this);
        return this;
    }
});

directory.views.codedItemView = Backbone.View.extend({
    tagName: "li",

    initialize: function() {
        this.template = _.template(directory.utils.templateLoader.get('coded-item'));
    },

    render: function(eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        return this;
    }
});

Thanks for looking!

4

1 回答 1

1

This might not be the solution to your problem, but there are certainly some changes that I would suggest to make your code cleaner.

directory.views.codedOptionsPage = Backbone.View.extend({
    events: {
        "click a.add-item": "add"
    },
    initialize: function () {
        this.template = _.template(directory.utils.templateLoader.get('coded-options-page'));
        // You seem to render the Whole List View when you add a new Code Model
        // to the collection .. I think it should be moved to the child View which render the 
        // code Model
        //this.model.attributes.codes.bind('add', this.render);
        // Use .get('name') to access the attributes
        // Save it to a Variable so that you can pass this in
        this.codesCollection = this.model.get('codes');
        _.bindAll(this, "add");
    },
    add: function () {
        var code = new directory.models.code();
        this.codesCollection.add(code);
    },
    // Create a new method and move the rendering of listView here
    // as it seperated the functionality of the methods
    renderListView: function () {
        var listView = new directory.views.codedItemListView({
            el: $('ul.codes', this.el),
            model: this.model,
            // Pass in the collection
            collection: this.codesCollection
        });
        listView.render();
    },
    render: function (eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        this.renderListView();
        return this;
    }
});

directory.views.codedItemListView = Backbone.View.extend({
    el: 'ul.code-list',
    initialize: function () {
        // Moved the Add event on collection to this view
        // Use listenTo to attach events instead of bind and on
        this.listenTo(this.collection, 'add', this.renderCodedItem);
        // Replacing this with listen
        //this.model.bind("reset", this.render, this);
        this.listenTo(this.model, 'reset', this.render);
    },
    // Moved the rendering of the ItemView to a different method
    renderCodedItem: function (code) {
        var li = new directory.views.codedItemView({
            model: code
        });
        // Appending the el of the ItemView to code-list
        $("#code-list").append(li.el);
        // Render the item
        li.render();
    },
    render: function (eventName) {
        // Use this.$el to access the el element
        //$(this.el).empty();
        this.$el.empty();
        _.each(this.collection, function (code) {
            this.renderCodedItem(code);
        }, this);
        return this;
    }
});

directory.views.codedItemView = Backbone.View.extend({
    tagName: "li",
    initialize: function () {
        this.template = _.template(directory.utils.templateLoader.get('coded-item'));
    },
    render: function (eventName) {
        $(this.el).html(this.template(this.model.toJSON()));
        return this;
    }
});
于 2013-07-30T01:58:50.407 回答