diff --git a/integration/_payload-limits.json b/integration/_payload-limits.json index f7ecc299a3..34682d492d 100644 --- a/integration/_payload-limits.json +++ b/integration/_payload-limits.json @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 266648, + "main-es2015": 267182, "polyfills-es2015": 36808, "5-es2015": 751 } diff --git a/packages/common/src/directives/ng_class_impl.ts b/packages/common/src/directives/ng_class_impl.ts index ef48b17292..6232cfdf6f 100644 --- a/packages/common/src/directives/ng_class_impl.ts +++ b/packages/common/src/directives/ng_class_impl.ts @@ -26,7 +26,7 @@ export abstract class NgClassImpl { } @Injectable() -export class NgClassR2Impl implements NgClassImpl { +export class NgClassR2Impl extends NgClassImpl { // TODO(issue/24571): remove '!'. private _iterableDiffer !: IterableDiffer| null; // TODO(issue/24571): remove '!'. @@ -37,7 +37,9 @@ export class NgClassR2Impl implements NgClassImpl { constructor( private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers, - private _ngEl: ElementRef, private _renderer: Renderer2) {} + private _ngEl: ElementRef, private _renderer: Renderer2) { + super(); + } getValue() { return null; } @@ -48,7 +50,8 @@ export class NgClassR2Impl implements NgClassImpl { this._applyClasses(this._rawClass); } - setNgClass(value: string) { + + setNgClass(value: string|string[]|Set|{[klass: string]: any}) { this._removeClasses(this._rawClass); this._applyClasses(this._initialClasses); @@ -96,7 +99,7 @@ export class NgClassR2Impl implements NgClassImpl { this._toggleClass(record.item, true); } else { throw new Error( - `NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`); + `NgClass can only toggle CSS classes expressed as strings, got: ${stringify(record.item)}`); } }); @@ -150,13 +153,13 @@ export class NgClassR2Impl implements NgClassImpl { } @Injectable() -export class NgClassR3Impl implements NgClassImpl { +export class NgClassR3Impl extends NgClassImpl { private _value: {[key: string]: boolean}|null = null; - private _ngClassDiffer = new StylingDiffer<{[key: string]: boolean}|null>( + private _ngClassDiffer = new StylingDiffer<{[key: string]: true}>( 'NgClass', StylingDifferOptions.TrimProperties| StylingDifferOptions.AllowSubKeys| StylingDifferOptions.AllowStringValue|StylingDifferOptions.ForceAsMap); - private _classStringDiffer: StylingDiffer<{[key: string]: boolean}>|null = null; + private _classStringDiffer: StylingDiffer<{[key: string]: true}>|null = null; getValue() { return this._value; } @@ -168,26 +171,23 @@ export class NgClassR3Impl implements NgClassImpl { this._classStringDiffer = this._classStringDiffer || new StylingDiffer('class', StylingDifferOptions.AllowStringValue | StylingDifferOptions.ForceAsMap); - this._classStringDiffer.setValue(value); + this._classStringDiffer.setInput(value); } setNgClass(value: string|string[]|Set|{[klass: string]: any}) { - this._ngClassDiffer.setValue(value); + this._ngClassDiffer.setInput(value); } applyChanges() { - const classChanged = - this._classStringDiffer ? this._classStringDiffer.hasValueChanged() : false; - const ngClassChanged = this._ngClassDiffer.hasValueChanged(); + const classChanged = this._classStringDiffer ? this._classStringDiffer.updateValue() : false; + const ngClassChanged = this._ngClassDiffer.updateValue(); if (classChanged || ngClassChanged) { - let value = this._ngClassDiffer.value; - if (this._classStringDiffer) { - let classValue = this._classStringDiffer.value; - if (classValue) { - value = value ? {...classValue, ...value} : classValue; - } - } - this._value = value; + let ngClassValue = this._ngClassDiffer.value; + let classValue = this._classStringDiffer ? this._classStringDiffer.value : null; + + // merge classValue and ngClassValue and set value + this._value = (classValue && ngClassValue) ? {...classValue, ...ngClassValue} : + classValue || ngClassValue; } } } diff --git a/packages/common/src/directives/ng_style_impl.ts b/packages/common/src/directives/ng_style_impl.ts index 5c0e87566d..e3c720ceaa 100644 --- a/packages/common/src/directives/ng_style_impl.ts +++ b/packages/common/src/directives/ng_style_impl.ts @@ -83,16 +83,16 @@ export class NgStyleR2Impl implements NgStyleImpl { @Injectable() export class NgStyleR3Impl implements NgStyleImpl { private _differ = - new StylingDiffer<{[key: string]: any}|null>('NgStyle', StylingDifferOptions.AllowUnits); + new StylingDiffer<{[key: string]: any}>('NgStyle', StylingDifferOptions.AllowUnits); private _value: {[key: string]: any}|null = null; getValue() { return this._value; } - setNgStyle(value: {[key: string]: any}|null) { this._differ.setValue(value); } + setNgStyle(value: {[key: string]: any}|null) { this._differ.setInput(value); } applyChanges() { - if (this._differ.hasValueChanged()) { + if (this._differ.updateValue()) { this._value = this._differ.value; } } diff --git a/packages/common/src/directives/styling_differ.ts b/packages/common/src/directives/styling_differ.ts index 792ab1a44a..bb4f13910c 100644 --- a/packages/common/src/directives/styling_differ.ts +++ b/packages/common/src/directives/styling_differ.ts @@ -13,7 +13,7 @@ * of how [style] and [class] behave in Angular. * * The differences are: - * - ngStyle and ngClass both **watch** their binding values for changes each time CD runs + * - ngStyle and ngClass both **deep-watch** their binding values for changes each time CD runs * while [style] and [class] bindings do not (they check for identity changes) * - ngStyle allows for unit-based keys (e.g. `{'max-width.px':value}`) and [style] does not * - ngClass supports arrays of class values and [class] only accepts map and string values @@ -24,8 +24,9 @@ * and unnecessary. Instead, ngClass and ngStyle should have their input values be converted * into something that the core-level [style] and [class] bindings understand. * - * This [StylingDiffer] class handles this conversion by creating a new input value each time - * the inner representation of the binding value have changed. + * This [StylingDiffer] class handles this conversion by creating a new output value each time + * the input value of the binding value has changed (either via identity change or deep collection + * content change). * * ## Why do we care about ngStyle/ngClass? * The styling algorithm code (documented inside of `render3/interfaces/styling.ts`) needs to @@ -40,8 +41,8 @@ * - If ngStyle/ngClass is used in combination with [style]/[class] bindings then the * styles and classes would fall out of sync and be applied and updated at * inconsistent times - * - Both ngClass/ngStyle do not respect [class.name] and [style.prop] bindings - * (they will write over them given the right combination of events) + * - Both ngClass/ngStyle should respect [class.name] and [style.prop] bindings (and not arbitrarily + * overwrite their changes) * * ``` *