1
votes

I'm kind of new to Backbone and I have some code that is supposed to listen to a property change and trigger a function. Everything I'm seeing online about how to use listenTo seems to cover this exact situation so I don't know what I'm overlooking.

To try to get a better understanding I made a much less complicated sample that just contains a model, a view and sets up the listenTo. The problem still arises in that smaller sample.

I've put some code in to log which functions are being fired and in what order. When I see when running this code is pretty much the order that I expect, except that onSomethingChanged is never called.

var AppView = Backbone.View.extend({
    initialize: function(){
        this.listenTo(this.model, 'change:hasName', this.onSomethingChanged);

        $('#steps').append('<li>AppView initialize</li>');
        this.model.setName('Test Name');
    },
    onSomethingChanged: function(){
        $('#steps').append('<li>AppView onSomethingChanged</li>');
    }
});

var SomeModel = Backbone.Model.extend({
    initialize: function(){
        this.bind('change:name', this.onNameChanged, this);
        this.set({'name': 'Nothing'});

        $('#steps').append('<li>SomeModel initialize</li>');
    },
    onNameChanged: function(status, name){
        this.set({'hasName': name != null});

        $('#steps').append('<li>SomeModel onNameChanged</li>');
    },
    setName: function(name){
        this.set({'name': name});

        $('#steps').append('<li>SomeModel setName</li>');
    }
});

$(function(){
    window.app = new AppView({model: new SomeModel()});
});

I've set it up in a fiddle here: https://jsfiddle.net/hamveefz and it shows the order as this:

  1. SomeModel onNameChanged
  2. SomeModel initialize
  3. AppView initialize
  4. SomeModel onNameChanged
  5. SomeModel setName

I assumed that in SomeModel's onNameChanged function where it then does a 'this.set' with the hasName property would automatically cause the listener for changes to hasName to be invoked.

Can someone explain to me where I'm going wrong here?

1

1 Answers

3
votes

Calling set doesn't necessarily change anything. For example, this:

model.set('property', model.get('property'));

won't trigger a 'change' event because it doesn't actually do anything.

Your model's initialize sets the name:

this.set({'name': 'Nothing'});

The 'change:name' handler is bound by that point so onNameChanged will be called and hasName will be set to true.

Then the view is built and its initialize will:

this.listenTo(this.model, 'change:hasName', this.onSomethingChanged);
this.model.setName('Test Name');

You're thinking that setName should trigger the 'change:hasName' event but it won't. The hasName attribute will already be true at this point and you'll be doing a set('hasName', true) so it won't change.

You'd be better off leaving the name as null or undefined until it actually gets set. You'd remove the this.set({'name': 'Nothing'}); from the model's initialize and add:

defaults: {
    name: null,
    hasName: false
}

Then adjust to view (and its template) to handle the "no name" case.