Add declarataive event handlers. Now inside the <template> of a SkyElement you can use on-eventName="method" on any element to add event listeners. For example you can write <sky-button on-click="handleClick"> and then define handleClick(event) on the element class that contains the button. In adding this and tests I also realized that property bindings were not setup on the initial call to bind(), which is now fixed in this patch (See change to Node.prototype.bind). R=eseidel@google.com, rafaelw@chromium.org Review URL: https://codereview.chromium.org/812713005
diff --git a/sky/examples/widgets/index.sky b/sky/examples/widgets/index.sky index 88e63ce..d102f85 100644 --- a/sky/examples/widgets/index.sky +++ b/sky/examples/widgets/index.sky
@@ -16,8 +16,9 @@ </style> <sky-box title='Buttons'> - <sky-button id='button'>Button</sky-button> + <sky-button id='button' on-click='handleClick'>Button</sky-button> <div>highlight: {{ myButton.highlight }}</div> + <div>clickCount: {{ clickCount }}</div> </sky-box> <sky-box title='Checkboxes'> @@ -47,6 +48,10 @@ attached() { this.myButton = this.shadowRoot.getElementById('button'); this.myCheckbox = this.shadowRoot.getElementById('checkbox'); + this.clickCount = 0; + } + handleClick(event) { + this.clickCount++; } }.register(); </script>
diff --git a/sky/framework/sky-element/TemplateBinding.sky b/sky/framework/sky-element/TemplateBinding.sky index 65d9bb5..1000094 100644 --- a/sky/framework/sky-element/TemplateBinding.sky +++ b/sky/framework/sky-element/TemplateBinding.sky
@@ -14,7 +14,7 @@ return; } - observable.open(function(value) { + this[name] = observable.open(function(value) { self[name] = value; }); @@ -544,6 +544,16 @@ return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn); } +function addEventHandler(element, name, method) { + element.addEventListener(name, function(event) { + var scope = element.ownerScope; + var host = scope.host; + var handler = host && host[method]; + if (handler instanceof Function) + return handler.call(host, event); + }); +} + function parseAttributeBindings(element, prepareBindingFn) { var bindings = []; var ifFound = false; @@ -560,6 +570,13 @@ continue; } + if (name.startsWith('on-')) { + if (!bindings.eventHandlers) + bindings.eventHandlers = new Map(); + bindings.eventHandlers.set(name.substring(3), value); + continue; + } + var tokens = parseMustaches(value, name, element, prepareBindingFn); if (!tokens) @@ -618,6 +635,12 @@ clone.setDelegate_(delegate); } + if (bindings.eventHandlers) { + bindings.eventHandlers.forEach(function(handler, eventName) { + addEventHandler(clone, eventName, handler); + }); + } + processBindings(clone, bindings, model, instanceBindings); return clone; }
diff --git a/sky/tests/framework/templates-expected.txt b/sky/tests/framework/templates-expected.txt new file mode 100644 index 0000000..c79c875 --- /dev/null +++ b/sky/tests/framework/templates-expected.txt
@@ -0,0 +1,7 @@ +Running 3 tests +ok 1 SkyElement templates should stamp when the element is inserted +ok 2 SkyElement templates should connect data binding +ok 3 SkyElement templates should connect event handlers +3 tests +3 pass +0 fail
diff --git a/sky/tests/framework/templates.sky b/sky/tests/framework/templates.sky new file mode 100644 index 0000000..c0a9fa6 --- /dev/null +++ b/sky/tests/framework/templates.sky
@@ -0,0 +1,54 @@ +<sky> +<import src="/sky/tests//resources/chai.sky" /> +<import src="/sky/tests/resources/mocha.sky" /> +<import src="/sky/tests/resources/test-element.sky" as="TestElement" /> + +<div id="sandbox"></div> + +<script> +describe("SkyElement templates", function() { + var element; + var sandbox = document.getElementById("sandbox"); + + beforeEach(function() { + element = new TestElement(); + }); + afterEach(function() { + element.remove(); + }); + + it("should stamp when the element is inserted", function() { + assert.isNull(element.shadowRoot); + sandbox.appendChild(element); + assert.instanceOf(element.shadowRoot, ShadowRoot); + assert.ok(element.shadowRoot.getElementById("inside")); + }); + + it("should connect data binding", function(done) { + sandbox.appendChild(element); + var inside = element.shadowRoot.getElementById("inside"); + Promise.resolve().then(function() { + assert.equal(inside.textContent, 10); + assert.equal(inside.attr, 10); + element.value = 20; + }).then(function() { + assert.equal(inside.textContent, 20); + assert.equal(inside.attr, 20); + done(); + }).catch(function(e) { + done(e); + }); + }); + + it("should connect event handlers", function() { + sandbox.appendChild(element); + var inside = element.shadowRoot.getElementById("inside"); + inside.dispatchEvent(new CustomEvent("wrong-event")); + assert.isNull(element.lastEvent); + var event = new CustomEvent("test-event"); + inside.dispatchEvent(event); + assert.equal(element.lastEvent, event); + }); +}); +</script> +</sky> \ No newline at end of file
diff --git a/sky/tests/resources/test-element.sky b/sky/tests/resources/test-element.sky new file mode 100644 index 0000000..c852cd1 --- /dev/null +++ b/sky/tests/resources/test-element.sky
@@ -0,0 +1,23 @@ +<!-- +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +--> +<import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" /> + +<sky-element name="test-element"> +<template> + <div id="inside" on-test-event="handleEvent" attr="{{ value }}">{{ value }}</div> +</template> +<script> +module.exports = class extends SkyElement { + created() { + this.lastEvent = null; + this.value = 10; + } + handleEvent(event) { + this.lastEvent = event; + } +}.register(); +</script> +</sky-element>