Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 94c3522

Browse files
jbdeboervsavkin
authored andcommitted
feat(web components): Support custom events for element property binding
Closes #1449 Closes #1453
1 parent 059b76e commit 94c3522

File tree

4 files changed

+76
-17
lines changed

4 files changed

+76
-17
lines changed

lib/core/annotation_src.dart

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,28 @@ abstract class Directive {
203203
*/
204204
final List<String> exportExpressions;
205205

206+
/**
207+
* Event names to listen to during Web Component two-way binding.
208+
*
209+
* To support web components efficiently, Angular only reads element
210+
* bindings when specific events are fired. By default, Angular listens
211+
* to 'change'. Adding events names to this listen will cause Angular
212+
* to listen to those events instead.
213+
*
214+
* The name is intentionally long: this should be rarely used and therefore
215+
* it is important that it is self-documenting.
216+
*/
217+
final List<String> updateBoundElementPropertiesOnEvents;
218+
206219
const Directive({
207220
this.selector,
208221
this.children,
209222
this.visibility,
210223
this.module,
211224
this.map: const {},
212225
this.exportExpressions: const [],
213-
this.exportExpressionAttrs: const []
226+
this.exportExpressionAttrs: const [],
227+
this.updateBoundElementPropertiesOnEvents
214228
});
215229

216230
toString() => selector;
@@ -282,15 +296,17 @@ class Component extends Directive {
282296
exportExpressions,
283297
exportExpressionAttrs,
284298
this.useShadowDom,
285-
this.useNgBaseCss: true})
286-
: _cssUrls = cssUrl,
299+
this.useNgBaseCss: true,
300+
updateBoundElementPropertiesOnEvents
301+
}) : _cssUrls = cssUrl,
287302
super(selector: selector,
288303
children: Directive.COMPILE_CHILDREN,
289304
visibility: visibility,
290305
map: map,
291306
module: module,
292307
exportExpressions: exportExpressions,
293-
exportExpressionAttrs: exportExpressionAttrs);
308+
exportExpressionAttrs: exportExpressionAttrs,
309+
updateBoundElementPropertiesOnEvents: updateBoundElementPropertiesOnEvents);
294310

295311
List<String> get cssUrls => _cssUrls == null ?
296312
const [] :
@@ -309,7 +325,8 @@ class Component extends Directive {
309325
exportExpressions: exportExpressions,
310326
exportExpressionAttrs: exportExpressionAttrs,
311327
useShadowDom: useShadowDom,
312-
useNgBaseCss: useNgBaseCss);
328+
useNgBaseCss: useNgBaseCss,
329+
updateBoundElementPropertiesOnEvents: updateBoundElementPropertiesOnEvents);
313330
}
314331

315332
/**
@@ -332,14 +349,16 @@ class Decorator extends Directive {
332349
DirectiveBinderFn module,
333350
visibility,
334351
exportExpressions,
335-
exportExpressionAttrs})
352+
exportExpressionAttrs,
353+
updateBoundElementPropertiesOnEvents})
336354
: super(selector: selector,
337355
children: children,
338356
visibility: visibility,
339357
map: map,
340358
module: module,
341359
exportExpressions: exportExpressions,
342-
exportExpressionAttrs: exportExpressionAttrs);
360+
exportExpressionAttrs: exportExpressionAttrs,
361+
updateBoundElementPropertiesOnEvents: updateBoundElementPropertiesOnEvents);
343362

344363
Directive _cloneWithNewMap(newMap) =>
345364
new Decorator(
@@ -349,7 +368,8 @@ class Decorator extends Directive {
349368
selector: selector,
350369
visibility: visibility,
351370
exportExpressions: exportExpressions,
352-
exportExpressionAttrs: exportExpressionAttrs);
371+
exportExpressionAttrs: exportExpressionAttrs,
372+
updateBoundElementPropertiesOnEvents: updateBoundElementPropertiesOnEvents);
353373
}
354374

355375
/**

lib/core_dom/element_binder.dart

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ class ElementBinder {
5959
bool get shouldCompileChildren =>
6060
childMode == Directive.COMPILE_CHILDREN;
6161

62+
List<String> _bindAssignablePropsOnCache;
63+
List<String> get _bindAssignablePropsOn {
64+
if (_bindAssignablePropsOnCache != null) return _bindAssignablePropsOnCache;
65+
_bindAssignablePropsOnCache = [];
66+
_usableDirectiveRefs.forEach((DirectiveRef ref) {
67+
var eventNames = ref.annotation.updateBoundElementPropertiesOnEvents;
68+
if (eventNames != null) {
69+
_bindAssignablePropsOnCache.addAll(eventNames);
70+
}
71+
});
72+
if (_bindAssignablePropsOnCache.isEmpty) _bindAssignablePropsOnCache.add('change');
73+
return _bindAssignablePropsOnCache;
74+
}
75+
6276
var _directiveCache;
6377
List<DirectiveRef> get _usableDirectiveRefs {
6478
if (_directiveCache != null) return _directiveCache;
@@ -308,13 +322,11 @@ class ElementBinder {
308322
// due to https://code.google.com/p/dart/issues/detail?id=17406
309323
// we have to manually run the zone.
310324
var zone = Zone.current;
311-
node.addEventListener('change', (_) =>
312-
zone.run(() =>
313-
bindAssignableProps.forEach((propAndExp) =>
314-
propAndExp[1].assign(scope.context, jsNode[propAndExp[0]])
315-
)
316-
)
317-
);
325+
_bindAssignablePropsOn.forEach((String eventName) =>
326+
node.addEventListener(eventName, (_) =>
327+
zone.run(() =>
328+
bindAssignableProps.forEach((propAndExp) =>
329+
propAndExp[1].assign(scope.context, jsNode[propAndExp[0]])))));
318330
}
319331

320332
if (onEvents.isNotEmpty) {

test/core/annotation_src_spec.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ void main() => describe('annotations', () {
4444
visibility: Directive.LOCAL_VISIBILITY,
4545
exportExpressions: [],
4646
exportExpressionAttrs: [],
47-
useShadowDom: true
47+
useShadowDom: true,
48+
updateBoundElementPropertiesOnEvents: []
4849
);
4950

5051
// Check that no fields are null
@@ -64,7 +65,8 @@ void main() => describe('annotations', () {
6465
module: (i){},
6566
visibility: Directive.LOCAL_VISIBILITY,
6667
exportExpressions: [],
67-
exportExpressionAttrs: []
68+
exportExpressionAttrs: [],
69+
updateBoundElementPropertiesOnEvents: []
6870
);
6971

7072
// Check that no fields are null

test/core_dom/web_components_spec.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ main() {
4343
beforeEach((TestBed tb) {
4444
_ = tb;
4545
});
46+
47+
beforeEachModule((Module m) {
48+
m.bind(TestsTwoWayCustom);
49+
});
4650

4751
it('should create custom elements', () {
4852
registerElement('tests-basic', {'prop-x': 6});
@@ -89,5 +93,26 @@ main() {
8993

9094
expect(_.rootScope.context['x']).toEqual(6);
9195
});
96+
97+
it('should support two-way bindings for components that trigger a defined event', () {
98+
registerElement('tests-twoway-custom', {});
99+
compileAndUpgrade('<tests-twoway-custom bind-prop="x"></tests-twoway-custom>');
100+
101+
setCustomProp('prop', 6);
102+
_.rootElement.dispatchEvent(new Event.eventType('CustomEvent', 'x-change'));
103+
104+
expect(_.rootScope.context['x']).toEqual(6);
105+
106+
// The change event should not cause an update
107+
setCustomProp('prop', 7);
108+
_.rootElement.dispatchEvent(new Event.eventType('CustomEvent', 'change'));
109+
expect(_.rootScope.context['x']).toEqual(6);
110+
});
92111
});
93112
}
113+
114+
@Decorator(
115+
selector: 'tests-twoway-custom',
116+
updateBoundElementPropertiesOnEvents: const ['x-change']
117+
)
118+
class TestsTwoWayCustom {}

0 commit comments

Comments
 (0)