From 95dedf3e11f2ddfe789198d0b037024007dfabf6 Mon Sep 17 00:00:00 2001 From: Andrew Mitchell Date: Wed, 12 Apr 2017 11:59:21 -0500 Subject: [PATCH] feat(events): listen and notify Angular of object and array mutations Fixes #9 --- src/events/polymer-property.ts | 28 ++++++++++++++++--------- src/events/polymer.directive.ts | 29 ++++++++++++++++++-------- src/origami.ts | 2 ++ src/util/Polymer.ts | 37 +++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/events/polymer-property.ts b/src/events/polymer-property.ts index 66a6bf8..534be70 100644 --- a/src/events/polymer-property.ts +++ b/src/events/polymer-property.ts @@ -1,3 +1,4 @@ +import { } from '../util/Polymer'; import { OnPolymerChange } from './on-polymer-change'; export function PolymerProperty(): PropertyDecorator { @@ -18,18 +19,25 @@ export function PolymerProperty(): PropertyDecorator { return value; } }, - set(valueOrEvent: any|CustomEvent) { - let newValue = unwrapPolymerEvent(valueOrEvent); - if (desc && desc.set) { - desc.set(newValue); - } + set(event: any|CustomEvent) { + if (event instanceof CustomEvent && event.detail.path) { + // Object or Array mutation, we need to tell Angular that things have changed + if ((this).onPolymerChange && event instanceof CustomEvent) { + (this).onPolymerChange(propertyKey, value, event.detail); + } + } else { + let newValue = unwrapPolymerEvent(event); + if (desc && desc.set) { + desc.set(newValue); + } - if (newValue !== value) { - // Even if there is a setter, we still keep a copy to determine if a change happens - value = newValue; + if (newValue !== value) { + // Even if there is a setter, we still keep a copy to determine if a change happens + value = newValue; - if ((this).onPolymerChange && valueOrEvent instanceof CustomEvent) { - (this).onPolymerChange(propertyKey, valueOrEvent, valueOrEvent.detail); + if ((this).onPolymerChange && event instanceof CustomEvent) { + (this).onPolymerChange(propertyKey, event, event.detail); + } } } } diff --git a/src/events/polymer.directive.ts b/src/events/polymer.directive.ts index fec5afb..d10808d 100644 --- a/src/events/polymer.directive.ts +++ b/src/events/polymer.directive.ts @@ -12,16 +12,24 @@ export class PolymerDirective implements OnInit { ngOnInit() { const klass = getCustomElementClass(this.elementRef); if (klass) { - // Setup Polymer to Angular event mapping - let notify = []; - notify = notify.concat(...this.getNotifyProperties(klass.prototype.properties || {})); + const properties = {}; + this.copyKeysFrom(klass.prototype.properties, properties); if (klass.prototype.behaviors) { - klass.prototype.behaviors.forEach(behavior => { - notify = notify.concat(...this.getNotifyProperties(behavior.properties || {})); + klass.prototype.behaviors.map(behavior => { + return behavior.properties || []; + }).forEach(property => { + this.copyKeysFrom(property, properties); }); } - notify.forEach(property => { + // Listen for notify properties and Object/Array properties which may issue path changes + const changeable = Object.keys(properties).filter(propertyName => { + const property = properties[propertyName]; + return property.notify || property === Object || property.type === Object || + property === Array || property.type === Array; + }); + + changeable.forEach(property => { const eventName = `${window.Polymer.CaseMap.camelToDashCase(property)}-changed`; this.elementRef.nativeElement.addEventListener(eventName, event => { this.elementRef.nativeElement.dispatchEvent(new CustomEvent(`${property}Change`, { @@ -32,9 +40,12 @@ export class PolymerDirective implements OnInit { } } - private getNotifyProperties(properties: any): string[] { - return Object.keys(properties).filter(property => { - return properties[property].notify; + private copyKeysFrom(from: any, to: any): any { + Object.keys(from || {}).forEach(key => { + if (key[0] !== '_') { + // Only copy public properties + to[key] = from[key]; + } }); } } diff --git a/src/origami.ts b/src/origami.ts index 0876645..9c72cb8 100644 --- a/src/origami.ts +++ b/src/origami.ts @@ -5,3 +5,5 @@ export * from './forms/iron-control'; export * from './polymer.module'; export * from './style/custom-style.service'; export * from './templates/polymer-template'; +export * from './util/customElements'; +export * from './util/Polymer'; diff --git a/src/util/Polymer.ts b/src/util/Polymer.ts index ea00fbd..9c04eb8 100644 --- a/src/util/Polymer.ts +++ b/src/util/Polymer.ts @@ -3,10 +3,47 @@ export namespace Polymer { camelToDashCase(camel: string): string; dashToCamelCase(dash: string): string; } + + export type PathLike = string|Array; + + export interface Path { + isPath(path: string): boolean; + root(path: string): string; + isAncestor(base: string, path: string): boolean; + isDescendant(base: string, path: string): boolean; + translate(base: string, newBase: string, path: string): string; + matches(base: string, path: string): boolean; + normalize(path: PathLike): string; + split(path: PathLike): string[]; + get(root: any, path: PathLike, info?: any): any; + set(root: any, path: PathLike, info?: any): string; + } + + export interface Splice { + index: number; + addedCount: number; + removed: T[]; + object: T[]; + type: 'splice'; + } + + export interface PropertyEffects { + setProperties(props: Object); + notifySplices(path: PathLike, splices: Array>); + get(path: PathLike, root?: any): any; + set(path: PathLike, value: any, root?: any); + push(path: PathLike, ...items: T[]): number; + pop(path: PathLike): any; + splice(path: PathLike, start: number, deleteCount: number, ...items: T[]): T[]; + shift(path: PathLike): any; + unshift(path: PathLike, ...items: T[]): number; + notifyPath(path: PathLike, value?: any); + } } export interface Polymer { CaseMap: Polymer.CaseMap; + Path: Polymer.Path; } declare global {