Skip to content
This repository has been archived by the owner on Mar 11, 2024. It is now read-only.

Commit

Permalink
Added element unbinding to everything that binds to an element (so th…
Browse files Browse the repository at this point in the history
…ey stop firing after it's destroyed)

Renamed init to bindToModel and created the new method bindToElement for consistency
Added tests to all that use element binding
  • Loading branch information
nathanpalmer committed Aug 19, 2012
1 parent 9dab8a7 commit a96b1fc
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 59 deletions.
102 changes: 101 additions & 1 deletion spec/SpineDataBindSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,30 @@ describe("Spine.DataBind", function() {
expect(Person.constructor._callbacks["change"].length).toBe(current+5);
}
});

it("should bind element to change", function() {
binder = Controller.binders[0];
spyOn(binder, "change");

var firstNameInput = $('#firstName');
firstNameInput.val("Eric");
firstNameInput.trigger("change");

expect(binder.change).toHaveBeenCalled();
});

it("should unbind element when destroyed", function() {
binder = Controller.binders[0];
spyOn(binder, "change");

Controller.trigger("destroy-bindings");

var firstNameInput = $('#firstName');
firstNameInput.val("Eric");
firstNameInput.trigger("change");

expect(binder.change).not.toHaveBeenCalled();
});
};

describe("with bindings", function() {
Expand Down Expand Up @@ -420,6 +444,30 @@ describe("Spine.DataBind", function() {
expect(Person.constructor._callbacks["update"].length).toBe(3);
}
});

it("should bind element to change", function() {
binder = Controller.binders[1];
spyOn(binder, "change");

var phoneNumberSelect = $('#phoneNumbers');
phoneNumberSelect.find("option[value='555-101-9999']").attr("selected", "selected");
phoneNumberSelect.trigger("change");

expect(binder.change).toHaveBeenCalled();
});

it("should unbind element when destroyed", function() {
binder = Controller.binders[1];
spyOn(binder, "change");

Controller.trigger("destroy-bindings");

var phoneNumberSelect = $('#phoneNumbers');
phoneNumberSelect.find("option[value='555-101-9999']").attr("selected", "selected");
phoneNumberSelect.trigger("change");

expect(binder.change).not.toHaveBeenCalled();
});
};

describe("with bindings", function() {
Expand Down Expand Up @@ -575,6 +623,26 @@ describe("Spine.DataBind", function() {

expect(Person.firstName).toBe("Reset");
});

it("should bind element to click", function() {
binder = Controller.binders[2];
spyOn(binder, "change");

$('#reset').click();

expect(binder.change).toHaveBeenCalled();
});

it("should unbind element when destroyed", function() {
binder = Controller.binders[2];
spyOn(binder, "change");

Controller.trigger("destroy-bindings");

$('#reset').click();

expect(binder.change).not.toHaveBeenCalled();
});
};

describe("with bindings", function() {
Expand Down Expand Up @@ -1051,13 +1119,45 @@ describe("Spine.DataBind", function() {
expect(person.attr('checked')).toBe('checked');
});

it("should change model when changed on checkbox", function() {
it("should change element when model is updated", function() {
Person.person = false;
if (!Watch) Person.save();

var person = $('#person');
expect(person.attr('checked')).toBe(undefined);
});

it("should change model element is checked", function() {
var person = $('#person');
person.attr("checked",false);
person.trigger("change");

expect(Person.person).toBe(false);
});

it("should bind element to change", function() {
binder = Controller.binders[6];
spyOn(binder, "change");

var person = $('#person');
person.attr("checked",false);
person.trigger("change");

expect(binder.change).toHaveBeenCalled();
});

it("should unbind element when destroyed", function() {
binder = Controller.binders[6];
spyOn(binder, "change");

Controller.trigger("destroy-bindings");

var person = $('#person');
person.attr("checked",false);
person.trigger("change");

expect(binder.change).not.toHaveBeenCalled();
});
};

describe("with bindings", function() {
Expand Down
49 changes: 28 additions & 21 deletions src/spine.databind.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@ class Template
bind: (operators,model,controller,el,options) ->
unbind: (operators,model,controller,el,options) ->

init: (operators,model,controller,el,options,event) ->
bindToModel: (operators,model,controller,el,options,event = "update") ->
model.constructor.bind(event, binder = () => @update(operators,model,controller,el,options))
controller.bind("destroy-bindings", unbinder = (record) =>
#if record && model.eql(record)
model.constructor.unbind(event,binder)
controller.unbind("destroy-bindings", unbinder)
)

bindToElement: (operators,model,controller,el,options,event = "change") ->
el.bind(event+".spine-databind", binder = () => @change(operators,model,controller,el,options))
controller.bind("destroy-bindings", unbinder = (record) =>
el.unbind(event+".spine-databind", binder)
controller.unbind("destroy-bindings", unbinder)
)

get: (item,value,callback) ->
if typeof item[value] is "function"
result = item[value](callback)
Expand All @@ -32,12 +39,12 @@ class Update extends Template
keys: [ "text", "value" ]

bind: (operators,model,controller,el,options) ->
el.bind("change", => @change(operators,model,controller,el,options))
@bindToElement(operators,model,controller,el,options)

if options.watch
@init([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
@bindToModel([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
else
@init(operators,model,controller,el,options,"change")
@bindToModel(operators,model,controller,el,options,"change")

@update(operators,model,controller,el,options)

Expand Down Expand Up @@ -91,13 +98,13 @@ class Options extends Template
together = ops.concat(opsSelected)

# ops
@init(together,model,controller,el,options,"update["+ops[0].property+"]") if ops and ops.length is 1
@init(together,model,controller,el,options,"update["+opsSelected[0].property+"]") if opsSelected and opsSelected.length is 1
@bindToModel(together,model,controller,el,options,"update["+ops[0].property+"]") if ops and ops.length is 1
@bindToModel(together,model,controller,el,options,"update["+opsSelected[0].property+"]") if opsSelected and opsSelected.length is 1
else
@init(operators,model,controller,el,options,"update")
@bindToModel(operators,model,controller,el,options,"update")

if operators.some((e) -> e.name is "selectedOptions")
el.bind("change", => @change(operators,model,controller,el,options))
@bindToElement(operators,model,controller,el,options)

@update(operators,model,controller,el,options)

Expand Down Expand Up @@ -187,9 +194,9 @@ class Click extends Template
keys: [ "click" ]

bind: (operators,model,controller,el,options) ->
el.bind("click", => @click(operators,model,controller,el,options))
@bindToElement(operators,model,controller,el,options,"click")

click: (operators,model,controller,el,options) ->
change: (operators,model,controller,el,options) ->
binder = @
for operator in operators
binder.get(model,operator.property)
Expand All @@ -199,9 +206,9 @@ class Enable extends Template

bind: (operators,model,controller,el,options) ->
if options.watch
@init([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
@bindToModel([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
else
@init(operators,model,controller,el,options,"change")
@bindToModel(operators,model,controller,el,options,"change")

@update(operators,model,controller,el,options)

Expand All @@ -219,9 +226,9 @@ class Visible extends Template

bind: (operators,model,controller,el,options) ->
if options.watch
@init([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
@bindToModel([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
else
@init(operators,model,controller,el,options,"update")
@bindToModel(operators,model,controller,el,options,"update")

@update(operators,model,controller,el,options)

Expand All @@ -242,9 +249,9 @@ class Attribute extends Template
for operator in operators
json = JSON.parse(operator.property)
for own property of json
@init([operator],model,controller,el,options,"update["+json[property]+"]")
@bindToModel([operator],model,controller,el,options,"update["+json[property]+"]")
else
@init(operators,model,controller,el,options,"update")
@bindToModel(operators,model,controller,el,options,"update")

@update(operators,model,controller,el,options)

Expand All @@ -263,12 +270,12 @@ class Checked extends Template
keys: [ "checked" ]

bind: (operators,model,controller,el,options) ->
el.bind("change", => @change(operators,model,controller,el,options))
@bindToElement(operators,model,controller,el,options)

if options.watch
@init([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
@bindToModel([operator],model,controller,el,options,"update["+operator.property+"]") for operator in operators
else
@init(operators,model,controller,el,options,"change")
@bindToModel(operators,model,controller,el,options,"change")

@update(operators,model,controller,el,options)

Expand Down Expand Up @@ -399,7 +406,7 @@ DataBind =
element: selector
}

init = (element) ->
bindToModel = (element) ->
operators = element.operators
el = element.el

Expand Down Expand Up @@ -446,7 +453,7 @@ DataBind =
addElement(elements,info,info.value)

for element in elements
init(element)
bindToModel(element)

@bindingElements = bindingElements(elements)

Expand Down
Loading

0 comments on commit a96b1fc

Please sign in to comment.