diff --git a/src/compile.coffee b/src/compile.coffee index 58e495c..cc1c6ca 100644 --- a/src/compile.coffee +++ b/src/compile.coffee @@ -94,6 +94,15 @@ Property = else throw new SyntaxError("unkown lifecycle event '#{ast.name}'") + property: (ast, node, model, controller) -> + set = (value) -> + assignUnlessEqual(node.element, ast.name, value) + if ast.bound and ast.value + bindToProperty node, model, ast.value, (_, value) -> + set(value) + else + set(ast.value ? model) + Compile = element: (ast, model, controller) -> element = Serenade.document.createElement(ast.name) diff --git a/test/integration/properties.spec.coffee b/test/integration/properties.spec.coffee new file mode 100644 index 0000000..8ab3ed3 --- /dev/null +++ b/test/integration/properties.spec.coffee @@ -0,0 +1,57 @@ +require './../spec_helper' + +describe 'Bound properties', -> + beforeEach -> + @setupDom() + + it 'supports non-bound values', -> + model = {} + @render 'input[property:disabled="disabled"]', model + expect(@body).to.have.element('input[disabled]') + + it 'does not add bound property if value is undefined in model', -> + model = {} + @render 'input[property:disabled=@foo]', model + expect(@body).not.to.have.element('input[disabled]') + + it 'get bound properties from the model', -> + model = { disabled: true } + @render 'input[property:disabled=disabled]', model + expect(@body).to.have.element('input[disabled]') + + it 'changes bound properties as they are changed', -> + model = Serenade(disabled: true) + @render 'input[property:disabled=disabled]', model + expect(@body).to.have.element('input[disabled]') + model.disabled = false + expect(@body).not.to.have.element('input[disabled]') + + it 'does not access getter more than once when updating dom nodes', -> + model = {} + counter = 0 + + Serenade.defineProperty model, "counter", + get: -> + counter += 1 + @_counter + set: (value) -> + @_counter = value + + model.counter_property.trigger() + @render "input[property:disabled=@counter]", model + expect(counter).to.eql(2) + model.counter = true + expect(counter).to.eql(3) + model.counter = false + expect(counter).to.eql(4) + + it 'can set several property bindings', -> + model = Serenade(disabled: true, checked: true) + @render 'input[type="checkbox" property:disabled=@disabled property:checked=@checked]', model + expect(@body).to.have.element('input[disabled][checked]') + model.disabled = false + expect(@body).not.to.have.element('input[disabled][checked]') + expect(@body).to.have.element('input[checked]') + model.checked = false + expect(@body).not.to.have.element('input[checked]') + expect(@body).to.have.element('input') diff --git a/test/spec_helper.coffee b/test/spec_helper.coffee index 054a43a..7ed25ac 100644 --- a/test/spec_helper.coffee +++ b/test/spec_helper.coffee @@ -51,6 +51,8 @@ beforeEach -> @assert actual is text, "expected #{actual} to be #{text}" chai.Assertion::attribute = (name) -> @assert @obj.hasAttribute(name), "expected #{@obj} to have attribute #{name}" + chai.Assertion::property = (name) -> + @assert @obj[name], "expected #{@obj} to have property #{name}" chai.Assertion::triggerEvent = (event, options={}) -> args = null count = 0