From 69de7680f5e08165800d4db399949ea6bdff48d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Fri, 3 Jan 2020 15:30:40 -0800 Subject: [PATCH] Revert: "feat(ivy): convert [ngStyle] and [ngClass] to use ivy styling bindings" (#34616) This change reverts https://github.com/angular/angular/pull/28711 NOTE: This change deletes code and creates a BROKEN SHA. If reverting this SHA needs to be reverted with the next SHA to get back into a valid state. The change removes the fact that `NgStyle`/`NgClass` is special and colaborates with the `[style]`/`[class]` to merge its styles. By reverting to old behavior we have better backwards compatiblity since it is no longer treated special and simply overwrites the styles (same as VE) PR Close #34616 --- packages/common/src/common.ts | 2 +- packages/common/src/directives/index.ts | 6 +- packages/common/src/directives/ng_class.ts | 183 +++++--- .../common/src/directives/ng_class_impl.ts | 208 ---------- packages/common/src/directives/ng_style.ts | 106 ++--- .../common/src/directives/ng_style_impl.ts | 114 ----- .../common/src/directives/styling_differ.ts | 390 ------------------ packages/common/src/private_export.ts | 4 - .../test/directives/styling_differ_spec.ts | 123 ------ .../ngcc/test/integration/ngcc_spec.ts | 12 +- packages/core/src/render3/component.ts | 8 +- tools/public_api_guard/common/common.d.ts | 30 +- 12 files changed, 172 insertions(+), 1014 deletions(-) delete mode 100644 packages/common/src/directives/ng_class_impl.ts delete mode 100644 packages/common/src/directives/ng_style_impl.ts delete mode 100644 packages/common/src/directives/styling_differ.ts delete mode 100644 packages/common/test/directives/styling_differ_spec.ts diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 413b76fe0b..e9be42668a 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -20,7 +20,7 @@ export {registerLocaleData} from './i18n/locale_data'; export {Plural, NumberFormatStyle, FormStyle, Time, TranslationWidth, FormatWidth, NumberSymbol, WeekDay, getNumberOfCurrencyDigits, getCurrencySymbol, getLocaleDayPeriods, getLocaleDayNames, getLocaleMonthNames, getLocaleId, getLocaleEraNames, getLocaleWeekEndRange, getLocaleFirstDayOfWeek, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocalePluralCase, getLocaleTimeFormat, getLocaleNumberSymbol, getLocaleNumberFormat, getLocaleCurrencyCode, getLocaleCurrencyName, getLocaleCurrencySymbol, getLocaleDirection} from './i18n/locale_data_api'; export {parseCookieValue as ɵparseCookieValue} from './cookie'; export {CommonModule} from './common_module'; -export {NgClass, NgClassBase, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgStyleBase, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index'; +export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index'; export {DOCUMENT} from './dom_tokens'; export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index'; export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id'; diff --git a/packages/common/src/directives/index.ts b/packages/common/src/directives/index.ts index 214e2f9aa5..c776cb8ee2 100644 --- a/packages/common/src/directives/index.ts +++ b/packages/common/src/directives/index.ts @@ -7,18 +7,17 @@ */ import {Provider} from '@angular/core'; -import {NgClass, NgClassBase} from './ng_class'; +import {NgClass} from './ng_class'; import {NgComponentOutlet} from './ng_component_outlet'; import {NgForOf, NgForOfContext} from './ng_for_of'; import {NgIf, NgIfContext} from './ng_if'; import {NgPlural, NgPluralCase} from './ng_plural'; -import {NgStyle, NgStyleBase} from './ng_style'; +import {NgStyle} from './ng_style'; import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch'; import {NgTemplateOutlet} from './ng_template_outlet'; export { NgClass, - NgClassBase, NgComponentOutlet, NgForOf, NgForOfContext, @@ -27,7 +26,6 @@ export { NgPlural, NgPluralCase, NgStyle, - NgStyleBase, NgSwitch, NgSwitchCase, NgSwitchDefault, diff --git a/packages/common/src/directives/ng_class.ts b/packages/common/src/directives/ng_class.ts index b3f5235a26..63ddbe198e 100644 --- a/packages/common/src/directives/ng_class.ts +++ b/packages/common/src/directives/ng_class.ts @@ -5,69 +5,9 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵclassMap, ɵɵdefineDirective} from '@angular/core'; +import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer2, ɵisListLikeIterable as isListLikeIterable, ɵstringify as stringify} from '@angular/core'; -import {NgClassImpl, NgClassImplProvider} from './ng_class_impl'; - - - -/* - * NgClass (as well as NgStyle) behaves differently when loaded in the VE and when not. - * - * If the VE is present (which is for older versions of Angular) then NgClass will inject - * the legacy diffing algorithm as a service and delegate all styling changes to that. - * - * If the VE is not present then NgStyle will normalize (through the injected service) and - * then write all styling changes to the `[style]` binding directly (through a host binding). - * Then Angular will notice the host binding change and treat the changes as styling - * changes and apply them via the core styling instructions that exist within Angular. - */ - -// used when the VE is present -export const ngClassDirectiveDef__PRE_R3__ = undefined; - -// used when the VE is not present (note the directive will -// never be instantiated normally because it is apart of a -// base class) -export const ngClassDirectiveDef__POST_R3__ = ɵɵdefineDirective({ - type: function() {} as any, - selectors: null as any, - hostVars: 2, - hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) { - if (rf & ɵRenderFlags.Update) { - ɵɵclassMap(ctx.getValue()); - } - } -}); - -export const ngClassDirectiveDef = ngClassDirectiveDef__PRE_R3__; - -export const ngClassFactoryDef__PRE_R3__ = undefined; -export const ngClassFactoryDef__POST_R3__ = function() {}; -export const ngClassFactoryDef = ngClassFactoryDef__PRE_R3__; - -/** - * Serves as the base non-VE container for NgClass. - * - * While this is a base class that NgClass extends from, the - * class itself acts as a container for non-VE code to setup - * a link to the `[class]` host binding (via the static - * `ɵdir` property on the class). - * - * Note that the `ɵdir` property's code is switched - * depending if VE is present or not (this allows for the - * binding code to be set only for newer versions of Angular). - * - * @publicApi - */ -export class NgClassBase { - static ɵdir: any = ngClassDirectiveDef; - static ɵfac: any = ngClassFactoryDef; - - constructor(protected _delegate: NgClassImpl) {} - - getValue() { return this._delegate.getValue(); } -} +type NgClassSupportedTypes = string[] | Set| {[klass: string]: any} | null | undefined; /** * @ngModule CommonModule @@ -97,17 +37,124 @@ export class NgClassBase { * * @publicApi */ -@Directive({selector: '[ngClass]', providers: [NgClassImplProvider]}) -export class NgClass extends NgClassBase implements DoCheck { - constructor(delegate: NgClassImpl) { super(delegate); } +@Directive({selector: '[ngClass]'}) +export class NgClass implements DoCheck { + private _iterableDiffer: IterableDiffer|null = null; + private _keyValueDiffer: KeyValueDiffer|null = null; + private _initialClasses: string[] = []; + private _rawClass: NgClassSupportedTypes = null; + + constructor( + private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers, + private _ngEl: ElementRef, private _renderer: Renderer2) {} + @Input('class') - set klass(value: string) { this._delegate.setClass(value); } + set klass(value: string) { + this._removeClasses(this._initialClasses); + this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : []; + this._applyClasses(this._initialClasses); + this._applyClasses(this._rawClass); + } @Input('ngClass') set ngClass(value: string|string[]|Set|{[klass: string]: any}) { - this._delegate.setNgClass(value); + this._removeClasses(this._rawClass); + this._applyClasses(this._initialClasses); + + this._iterableDiffer = null; + this._keyValueDiffer = null; + + this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value; + + if (this._rawClass) { + if (isListLikeIterable(this._rawClass)) { + this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create(); + } else { + this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create(); + } + } } - ngDoCheck() { this._delegate.applyChanges(); } + ngDoCheck() { + if (this._iterableDiffer) { + const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]); + if (iterableChanges) { + this._applyIterableChanges(iterableChanges); + } + } else if (this._keyValueDiffer) { + const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any}); + if (keyValueChanges) { + this._applyKeyValueChanges(keyValueChanges); + } + } + } + + private _applyKeyValueChanges(changes: KeyValueChanges): void { + changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachRemovedItem((record) => { + if (record.previousValue) { + this._toggleClass(record.key, false); + } + }); + } + + private _applyIterableChanges(changes: IterableChanges): void { + changes.forEachAddedItem((record) => { + if (typeof record.item === 'string') { + this._toggleClass(record.item, true); + } else { + throw new Error( + `NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`); + } + }); + + changes.forEachRemovedItem((record) => this._toggleClass(record.item, false)); + } + + /** + * Applies a collection of CSS classes to the DOM element. + * + * For argument of type Set and Array CSS class names contained in those collections are always + * added. + * For argument of type Map CSS class name in the map's key is toggled based on the value (added + * for truthy and removed for falsy). + */ + private _applyClasses(rawClassVal: NgClassSupportedTypes) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + (rawClassVal).forEach((klass: string) => this._toggleClass(klass, true)); + } else { + Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, !!rawClassVal[klass])); + } + } + } + + /** + * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup + * purposes. + */ + private _removeClasses(rawClassVal: NgClassSupportedTypes) { + if (rawClassVal) { + if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { + (rawClassVal).forEach((klass: string) => this._toggleClass(klass, false)); + } else { + Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, false)); + } + } + } + + private _toggleClass(klass: string, enabled: boolean): void { + klass = klass.trim(); + if (klass) { + klass.split(/\s+/g).forEach(klass => { + if (enabled) { + this._renderer.addClass(this._ngEl.nativeElement, klass); + } else { + this._renderer.removeClass(this._ngEl.nativeElement, klass); + } + }); + } + } } diff --git a/packages/common/src/directives/ng_class_impl.ts b/packages/common/src/directives/ng_class_impl.ts deleted file mode 100644 index 6232cfdf6f..0000000000 --- a/packages/common/src/directives/ng_class_impl.ts +++ /dev/null @@ -1,208 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {ElementRef, Injectable, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer2, ɵisListLikeIterable as isListLikeIterable, ɵstringify as stringify} from '@angular/core'; - -import {StylingDiffer, StylingDifferOptions} from './styling_differ'; - -/** - * Used as a token for an injected service within the NgClass directive. - * - * NgClass behaves differenly whether or not VE is being used or not. If - * present then the legacy ngClass diffing algorithm will be used as an - * injected service. Otherwise the new diffing algorithm (which delegates - * to the `[class]` binding) will be used. This toggle behavior is done so - * via the ivy_switch mechanism. - */ -export abstract class NgClassImpl { - abstract setClass(value: string): void; - abstract setNgClass(value: string|string[]|Set|{[klass: string]: any}): void; - abstract applyChanges(): void; - abstract getValue(): {[key: string]: any}|null; -} - -@Injectable() -export class NgClassR2Impl extends NgClassImpl { - // TODO(issue/24571): remove '!'. - private _iterableDiffer !: IterableDiffer| null; - // TODO(issue/24571): remove '!'. - private _keyValueDiffer !: KeyValueDiffer| null; - private _initialClasses: string[] = []; - // TODO(issue/24571): remove '!'. - private _rawClass !: string[] | Set| {[klass: string]: any}; - - constructor( - private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers, - private _ngEl: ElementRef, private _renderer: Renderer2) { - super(); - } - - getValue() { return null; } - - setClass(value: string) { - this._removeClasses(this._initialClasses); - this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : []; - this._applyClasses(this._initialClasses); - this._applyClasses(this._rawClass); - } - - - setNgClass(value: string|string[]|Set|{[klass: string]: any}) { - this._removeClasses(this._rawClass); - this._applyClasses(this._initialClasses); - - this._iterableDiffer = null; - this._keyValueDiffer = null; - - this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value; - - if (this._rawClass) { - if (isListLikeIterable(this._rawClass)) { - this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create(); - } else { - this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create(); - } - } - } - - applyChanges() { - if (this._iterableDiffer) { - const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]); - if (iterableChanges) { - this._applyIterableChanges(iterableChanges); - } - } else if (this._keyValueDiffer) { - const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any}); - if (keyValueChanges) { - this._applyKeyValueChanges(keyValueChanges); - } - } - } - - private _applyKeyValueChanges(changes: KeyValueChanges): void { - changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue)); - changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue)); - changes.forEachRemovedItem((record) => { - if (record.previousValue) { - this._toggleClass(record.key, false); - } - }); - } - - private _applyIterableChanges(changes: IterableChanges): void { - changes.forEachAddedItem((record) => { - if (typeof record.item === 'string') { - this._toggleClass(record.item, true); - } else { - throw new Error( - `NgClass can only toggle CSS classes expressed as strings, got: ${stringify(record.item)}`); - } - }); - - changes.forEachRemovedItem((record) => this._toggleClass(record.item, false)); - } - - /** - * Applies a collection of CSS classes to the DOM element. - * - * For argument of type Set and Array CSS class names contained in those collections are always - * added. - * For argument of type Map CSS class name in the map's key is toggled based on the value (added - * for truthy and removed for falsy). - */ - private _applyClasses(rawClassVal: string[]|Set|{[klass: string]: any}) { - if (rawClassVal) { - if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { - (rawClassVal).forEach((klass: string) => this._toggleClass(klass, true)); - } else { - Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, !!rawClassVal[klass])); - } - } - } - - /** - * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup - * purposes. - */ - private _removeClasses(rawClassVal: string[]|Set|{[klass: string]: any}) { - if (rawClassVal) { - if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { - (rawClassVal).forEach((klass: string) => this._toggleClass(klass, false)); - } else { - Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, false)); - } - } - } - - private _toggleClass(klass: string, enabled: boolean): void { - klass = klass.trim(); - if (klass) { - klass.split(/\s+/g).forEach(klass => { - if (enabled) { - this._renderer.addClass(this._ngEl.nativeElement, klass); - } else { - this._renderer.removeClass(this._ngEl.nativeElement, klass); - } - }); - } - } -} - -@Injectable() -export class NgClassR3Impl extends NgClassImpl { - private _value: {[key: string]: boolean}|null = null; - private _ngClassDiffer = new StylingDiffer<{[key: string]: true}>( - 'NgClass', StylingDifferOptions.TrimProperties| - StylingDifferOptions.AllowSubKeys| - StylingDifferOptions.AllowStringValue|StylingDifferOptions.ForceAsMap); - private _classStringDiffer: StylingDiffer<{[key: string]: true}>|null = null; - - getValue() { return this._value; } - - setClass(value: string) { - // early exit incase the binding gets emitted as an empty value which - // means there is no reason to instantiate and diff the values... - if (!value && !this._classStringDiffer) return; - - this._classStringDiffer = this._classStringDiffer || - new StylingDiffer('class', - StylingDifferOptions.AllowStringValue | StylingDifferOptions.ForceAsMap); - this._classStringDiffer.setInput(value); - } - - setNgClass(value: string|string[]|Set|{[klass: string]: any}) { - this._ngClassDiffer.setInput(value); - } - - applyChanges() { - const classChanged = this._classStringDiffer ? this._classStringDiffer.updateValue() : false; - const ngClassChanged = this._ngClassDiffer.updateValue(); - if (classChanged || ngClassChanged) { - 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; - } - } -} - -// the implementation for both NgStyleR2Impl and NgStyleR3Impl are -// not ivy_switch'd away, instead they are only hooked up into the -// DI via NgStyle's directive's provider property. -export const NgClassImplProvider__PRE_R3__ = { - provide: NgClassImpl, - useClass: NgClassR2Impl -}; - -export const NgClassImplProvider__POST_R3__ = { - provide: NgClassImpl, - useClass: NgClassR3Impl -}; - -export const NgClassImplProvider = NgClassImplProvider__PRE_R3__; diff --git a/packages/common/src/directives/ng_style.ts b/packages/common/src/directives/ng_style.ts index c978d05b9a..73dab1a154 100644 --- a/packages/common/src/directives/ng_style.ts +++ b/packages/common/src/directives/ng_style.ts @@ -5,69 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵstyleMap} from '@angular/core'; +import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer2} from '@angular/core'; -import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl'; - - - -/* - * NgStyle (as well as NgClass) behaves differently when loaded in the VE and when not. - * - * If the VE is present (which is for older versions of Angular) then NgStyle will inject - * the legacy diffing algorithm as a service and delegate all styling changes to that. - * - * If the VE is not present then NgStyle will normalize (through the injected service) and - * then write all styling changes to the `[style]` binding directly (through a host binding). - * Then Angular will notice the host binding change and treat the changes as styling - * changes and apply them via the core styling instructions that exist within Angular. - */ - -// used when the VE is present -export const ngStyleDirectiveDef__PRE_R3__ = undefined; -export const ngStyleFactoryDef__PRE_R3__ = undefined; - -// used when the VE is not present (note the directive will -// never be instantiated normally because it is apart of a -// base class) -export const ngStyleDirectiveDef__POST_R3__ = ɵɵdefineDirective({ - type: function() {} as any, - selectors: null as any, - hostVars: 2, - hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) { - if (rf & ɵRenderFlags.Update) { - ɵɵstyleMap(ctx.getValue()); - } - } -}); - -export const ngStyleFactoryDef__POST_R3__ = function() {}; - -export const ngStyleDirectiveDef = ngStyleDirectiveDef__PRE_R3__; -export const ngStyleFactoryDef = ngStyleDirectiveDef__PRE_R3__; - -/** - * Serves as the base non-VE container for NgStyle. - * - * While this is a base class that NgStyle extends from, the - * class itself acts as a container for non-VE code to setup - * a link to the `[style]` host binding (via the static - * `ɵdir` property on the class). - * - * Note that the `ɵdir` property's code is switched - * depending if VE is present or not (this allows for the - * binding code to be set only for newer versions of Angular). - * - * @publicApi - */ -export class NgStyleBase { - static ɵdir: any = ngStyleDirectiveDef; - static ɵfac: any = ngStyleFactoryDef; - - constructor(protected _delegate: NgStyleImpl) {} - - getValue() { return this._delegate.getValue(); } -} /** * @ngModule CommonModule @@ -105,12 +44,45 @@ export class NgStyleBase { * * @publicApi */ -@Directive({selector: '[ngStyle]', providers: [NgStyleImplProvider]}) -export class NgStyle extends NgStyleBase implements DoCheck { - constructor(delegate: NgStyleImpl) { super(delegate); } +@Directive({selector: '[ngStyle]'}) +export class NgStyle implements DoCheck { + private _ngStyle: {[key: string]: string}|null = null; + private _differ: KeyValueDiffer|null = null; + + constructor( + private _ngEl: ElementRef, private _differs: KeyValueDiffers, private _renderer: Renderer2) {} @Input('ngStyle') - set ngStyle(value: {[klass: string]: any}|null) { this._delegate.setNgStyle(value); } + set ngStyle(values: {[klass: string]: any}|null) { + this._ngStyle = values; + if (!this._differ && values) { + this._differ = this._differs.find(values).create(); + } + } - ngDoCheck() { this._delegate.applyChanges(); } + ngDoCheck() { + if (this._differ) { + const changes = this._differ.diff(this._ngStyle !); + if (changes) { + this._applyChanges(changes); + } + } + } + + private _setStyle(nameAndUnit: string, value: string|number|null|undefined): void { + const [name, unit] = nameAndUnit.split('.'); + value = value != null && unit ? `${value}${unit}` : value; + + if (value != null) { + this._renderer.setStyle(this._ngEl.nativeElement, name, value as string); + } else { + this._renderer.removeStyle(this._ngEl.nativeElement, name); + } + } + + private _applyChanges(changes: KeyValueChanges): void { + changes.forEachRemovedItem((record) => this._setStyle(record.key, null)); + changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue)); + } } diff --git a/packages/common/src/directives/ng_style_impl.ts b/packages/common/src/directives/ng_style_impl.ts deleted file mode 100644 index e3c720ceaa..0000000000 --- a/packages/common/src/directives/ng_style_impl.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {ElementRef, Injectable, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer2} from '@angular/core'; - -import {StylingDiffer, StylingDifferOptions} from './styling_differ'; - -/** - * Used as a token for an injected service within the NgStyle directive. - * - * NgStyle behaves differenly whether or not VE is being used or not. If - * present then the legacy ngClass diffing algorithm will be used as an - * injected service. Otherwise the new diffing algorithm (which delegates - * to the `[style]` binding) will be used. This toggle behavior is done so - * via the ivy_switch mechanism. - */ -export abstract class NgStyleImpl { - abstract getValue(): {[key: string]: any}|null; - abstract setNgStyle(value: {[key: string]: any}|null): void; - abstract applyChanges(): void; -} - -@Injectable() -export class NgStyleR2Impl implements NgStyleImpl { - // TODO(issue/24571): remove '!'. - private _ngStyle !: {[key: string]: string}; - // TODO(issue/24571): remove '!'. - private _differ !: KeyValueDiffer; - - constructor( - private _ngEl: ElementRef, private _differs: KeyValueDiffers, private _renderer: Renderer2) {} - - getValue() { return null; } - - /** - * A map of style properties, specified as colon-separated - * key-value pairs. - * * The key is a style name, with an optional `.` suffix - * (such as 'top.px', 'font-style.em'). - * * The value is an expression to be evaluated. - */ - setNgStyle(values: {[key: string]: string}) { - this._ngStyle = values; - if (!this._differ && values) { - this._differ = this._differs.find(values).create(); - } - } - - /** - * Applies the new styles if needed. - */ - applyChanges() { - if (this._differ) { - const changes = this._differ.diff(this._ngStyle); - if (changes) { - this._applyChanges(changes); - } - } - } - - private _applyChanges(changes: KeyValueChanges): void { - changes.forEachRemovedItem((record) => this._setStyle(record.key, null)); - changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue)); - changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue)); - } - - private _setStyle(nameAndUnit: string, value: string|number|null|undefined): void { - const [name, unit] = nameAndUnit.split('.'); - value = value != null && unit ? `${value}${unit}` : value; - - if (value != null) { - this._renderer.setStyle(this._ngEl.nativeElement, name, value as string); - } else { - this._renderer.removeStyle(this._ngEl.nativeElement, name); - } - } -} - -@Injectable() -export class NgStyleR3Impl implements NgStyleImpl { - private _differ = - 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.setInput(value); } - - applyChanges() { - if (this._differ.updateValue()) { - this._value = this._differ.value; - } - } -} - -// the implementation for both NgClassR2Impl and NgClassR3Impl are -// not ivy_switch'd away, instead they are only hooked up into the -// DI via NgStyle's directive's provider property. -export const NgStyleImplProvider__PRE_R3__ = { - provide: NgStyleImpl, - useClass: NgStyleR2Impl -}; - -export const NgStyleImplProvider__POST_R3__ = { - provide: NgStyleImpl, - useClass: NgStyleR3Impl -}; - -export const NgStyleImplProvider = NgStyleImplProvider__PRE_R3__; diff --git a/packages/common/src/directives/styling_differ.ts b/packages/common/src/directives/styling_differ.ts deleted file mode 100644 index bb4f13910c..0000000000 --- a/packages/common/src/directives/styling_differ.ts +++ /dev/null @@ -1,390 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -/** - * Used to diff and convert ngStyle/ngClass instructions into [style] and [class] bindings. - * - * ngStyle and ngClass both accept various forms of input and behave differently than that - * of how [style] and [class] behave in Angular. - * - * The differences are: - * - 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 - * - ngClass allows for multiple className keys (space-separated) within an array or map - * (as the * key) while [class] only accepts a simple key/value map object - * - * Having Angular understand and adapt to all the different forms of behavior is complicated - * 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 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 - * respect and understand the styling values emitted through ngStyle and ngClass (when they - * are present and used in a template). - * - * Instead of having these directives manage styling on their own, they should be included - * into the Angular styling algorithm that exists for [style] and [class] bindings. - * - * Here's why: - * - * - 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 should respect [class.name] and [style.prop] bindings (and not arbitrarily - * overwrite their changes) - * - * ``` - * - *
...
- * - * - *
...
- * ``` - * - ngClass/ngStyle were written as a directives and made use of maps, closures and other - * expensive data structures which were evaluated each time CD runs - */ -export class StylingDiffer { - /** - * Normalized string map representing the last value set via `setValue()` or null if no value has - * been set or the last set value was null - */ - public readonly value: T|null = null; - - /** - * The last set value that was applied via `setValue()` - */ - private _inputValue: T|string|string[]|Set|null = null; - - /** - * The type of value that the `_lastSetValue` variable is - */ - private _inputValueType: StylingDifferValueTypes = StylingDifferValueTypes.Null; - - /** - * Whether or not the last value change occurred because the variable itself changed reference - * (identity) - */ - private _inputValueIdentityChangeSinceLastCheck = false; - - constructor(private _name: string, private _options: StylingDifferOptions) {} - - /** - * Sets the input value for the differ and updates the output value if necessary. - * - * @param value the new styling input value provided from the ngClass/ngStyle binding - */ - setInput(value: T|string[]|string|Set|null): void { - if (value !== this._inputValue) { - let type: StylingDifferValueTypes; - if (!value) { // matches empty strings, null, false and undefined - type = StylingDifferValueTypes.Null; - value = null; - } else if (Array.isArray(value)) { - type = StylingDifferValueTypes.Array; - } else if (value instanceof Set) { - type = StylingDifferValueTypes.Set; - } else if (typeof value === 'string') { - if (!(this._options & StylingDifferOptions.AllowStringValue)) { - throw new Error(this._name + ' string values are not allowed'); - } - type = StylingDifferValueTypes.String; - } else { - type = StylingDifferValueTypes.StringMap; - } - - this._inputValue = value; - this._inputValueType = type; - this._inputValueIdentityChangeSinceLastCheck = true; - this._processValueChange(true); - } - } - - /** - * Checks the input value for identity or deep changes and updates output value if necessary. - * - * This function can be called right after `setValue()` is called, but it can also be - * called incase the existing value (if it's a collection) changes internally. If the - * value is indeed a collection it will do the necessary diffing work and produce a - * new object value as assign that to `value`. - * - * @returns whether or not the value has changed in some way. - */ - updateValue(): boolean { - let valueHasChanged = this._inputValueIdentityChangeSinceLastCheck; - if (!this._inputValueIdentityChangeSinceLastCheck && - (this._inputValueType & StylingDifferValueTypes.Collection)) { - valueHasChanged = this._processValueChange(false); - } else { - // this is set to false in the event that the value is a collection. - // This way (if the identity hasn't changed), then the algorithm can - // diff the collection value to see if the contents have mutated - // (otherwise the value change was processed during the time when - // the variable changed). - this._inputValueIdentityChangeSinceLastCheck = false; - } - return valueHasChanged; - } - - /** - * Examines the last set value to see if there was a change in content. - * - * @param inputValueIdentityChanged whether or not the last set value changed in identity or not - * @returns `true` when the value has changed (either by identity or by shape if its a - * collection) - */ - private _processValueChange(inputValueIdentityChanged: boolean): boolean { - // if the inputValueIdentityChanged then we know that input has changed - let inputChanged = inputValueIdentityChanged; - - let newOutputValue: T|string|null = null; - const trimValues = (this._options & StylingDifferOptions.TrimProperties) ? true : false; - const parseOutUnits = (this._options & StylingDifferOptions.AllowUnits) ? true : false; - const allowSubKeys = (this._options & StylingDifferOptions.AllowSubKeys) ? true : false; - - switch (this._inputValueType) { - // case 1: [input]="string" - case StylingDifferValueTypes.String: { - if (inputValueIdentityChanged) { - // process string input only if the identity has changed since the strings are immutable - const keys = (this._inputValue as string).split(/\s+/g); - if (this._options & StylingDifferOptions.ForceAsMap) { - newOutputValue = {} as T; - for (let i = 0; i < keys.length; i++) { - (newOutputValue as any)[keys[i]] = true; - } - } else { - newOutputValue = keys.join(' '); - } - } - break; - } - // case 2: [input]="{key:value}" - case StylingDifferValueTypes.StringMap: { - const inputMap = this._inputValue as T; - const inputKeys = Object.keys(inputMap); - - if (!inputValueIdentityChanged) { - // if StringMap and the identity has not changed then output value must have already been - // initialized to a StringMap, so we can safely compare the input and output maps - inputChanged = mapsAreEqual(inputKeys, inputMap, this.value as T); - } - - if (inputChanged) { - newOutputValue = bulidMapFromStringMap( - trimValues, parseOutUnits, allowSubKeys, inputMap, inputKeys) as T; - } - break; - } - // case 3a: [input]="[str1, str2, ...]" - // case 3b: [input]="Set" - case StylingDifferValueTypes.Array: - case StylingDifferValueTypes.Set: { - const inputKeys = Array.from(this._inputValue as string[] | Set); - if (!inputValueIdentityChanged) { - const outputKeys = Object.keys(this.value !); - inputChanged = !keyArraysAreEqual(outputKeys, inputKeys); - } - if (inputChanged) { - newOutputValue = - bulidMapFromStringArray(this._name, trimValues, allowSubKeys, inputKeys) as T; - } - break; - } - // case 4: [input]="null|undefined" - default: - inputChanged = inputValueIdentityChanged; - newOutputValue = null; - break; - } - - if (inputChanged) { - // update the readonly `value` property by casting it to `any` first - (this as any).value = newOutputValue; - } - - return inputChanged; - } -} - -/** - * Various options that are consumed by the [StylingDiffer] class - */ -export const enum StylingDifferOptions { - None = 0b00000, // - TrimProperties = 0b00001, // - AllowSubKeys = 0b00010, // - AllowStringValue = 0b00100, // - AllowUnits = 0b01000, // - ForceAsMap = 0b10000, // -} - -/** - * The different types of inputs that the [StylingDiffer] can deal with - */ -const enum StylingDifferValueTypes { - Null = 0b0000, // - String = 0b0001, // - StringMap = 0b0010, // - Array = 0b0100, // - Set = 0b1000, // - Collection = 0b1110, // -} - - -/** - * @param trim whether the keys should be trimmed of leading or trailing whitespace - * @param parseOutUnits whether units like "px" should be parsed out of the key name and appended to - * the value - * @param allowSubKeys whether key needs to be subsplit by whitespace into multiple keys - * @param values values of the map - * @param keys keys of the map - * @return a normalized string map based on the input string map - */ -function bulidMapFromStringMap( - trim: boolean, parseOutUnits: boolean, allowSubKeys: boolean, - values: {[key: string]: string | null | true}, - keys: string[]): {[key: string]: string | null | true} { - const map: {[key: string]: string | null | true} = {}; - - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let value = values[key]; - - if (value !== undefined) { - if (typeof value !== 'boolean') { - value = '' + value; - } - // Map uses untrimmed keys, so don't trim until passing to `setMapValues` - setMapValues(map, trim ? key.trim() : key, value, parseOutUnits, allowSubKeys); - } - } - - return map; -} - -/** - * @param trim whether the keys should be trimmed of leading or trailing whitespace - * @param parseOutUnits whether units like "px" should be parsed out of the key name and appended to - * the value - * @param allowSubKeys whether key needs to be subsplit by whitespace into multiple keys - * @param values values of the map - * @param keys keys of the map - * @return a normalized string map based on the input string array - */ -function bulidMapFromStringArray( - errorPrefix: string, trim: boolean, allowSubKeys: boolean, - keys: string[]): {[key: string]: true} { - const map: {[key: string]: true} = {}; - - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - ngDevMode && assertValidValue(errorPrefix, key); - key = trim ? key.trim() : key; - setMapValues(map, key, true, false, allowSubKeys); - } - - return map; -} - -function assertValidValue(errorPrefix: string, value: any) { - if (typeof value !== 'string') { - throw new Error( - `${errorPrefix} can only toggle CSS classes expressed as strings, got: ${value}`); - } -} - -function setMapValues( - map: {[key: string]: unknown}, key: string, value: string | null | true, parseOutUnits: boolean, - allowSubKeys: boolean) { - if (allowSubKeys && key.indexOf(' ') > 0) { - const innerKeys = key.split(/\s+/g); - for (let j = 0; j < innerKeys.length; j++) { - setIndividualMapValue(map, innerKeys[j], value, parseOutUnits); - } - } else { - setIndividualMapValue(map, key, value, parseOutUnits); - } -} - -function setIndividualMapValue( - map: {[key: string]: unknown}, key: string, value: string | true | null, - parseOutUnits: boolean) { - if (parseOutUnits && typeof value === 'string') { - // parse out the unit (e.g. ".px") from the key and append it to the value - // e.g. for [width.px]="40" => ["width","40px"] - const unitIndex = key.indexOf('.'); - if (unitIndex > 0) { - const unit = key.substr(unitIndex + 1); // skip over the "." in "width.px" - key = key.substring(0, unitIndex); - value += unit; - } - } - map[key] = value; -} - - -/** - * Compares two maps and returns true if they are equal - * - * @param inputKeys value of `Object.keys(inputMap)` it's unclear if this actually performs better - * @param inputMap map to compare - * @param outputMap map to compare - */ -function mapsAreEqual( - inputKeys: string[], inputMap: {[key: string]: unknown}, - outputMap: {[key: string]: unknown}, ): boolean { - const outputKeys = Object.keys(outputMap); - - if (inputKeys.length !== outputKeys.length) { - return true; - } - - for (let i = 0, n = inputKeys.length; i <= n; i++) { - let key = inputKeys[i]; - if (key !== outputKeys[i] || inputMap[key] !== outputMap[key]) { - return true; - } - } - - return false; -} - - -/** - * Compares two Object.keys() arrays and returns true if they are equal. - * - * @param keyArray1 Object.keys() array to compare - * @param keyArray1 Object.keys() array to compare - */ -function keyArraysAreEqual(keyArray1: string[] | null, keyArray2: string[] | null): boolean { - if (!Array.isArray(keyArray1) || !Array.isArray(keyArray2)) { - return false; - } - - if (keyArray1.length !== keyArray2.length) { - return false; - } - - for (let i = 0; i < keyArray1.length; i++) { - if (keyArray1[i] !== keyArray2[i]) { - return false; - } - } - - return true; -} diff --git a/packages/common/src/private_export.ts b/packages/common/src/private_export.ts index 7801e395d0..038833ee04 100644 --- a/packages/common/src/private_export.ts +++ b/packages/common/src/private_export.ts @@ -6,9 +6,5 @@ * found in the LICENSE file at https://angular.io/license */ -export {ngClassDirectiveDef__POST_R3__ as ɵngClassDirectiveDef__POST_R3__, ngClassFactoryDef__POST_R3__ as ɵngClassFactoryDef__POST_R3__} from './directives/ng_class'; -export {NgClassImpl as ɵNgClassImpl, NgClassImplProvider__POST_R3__ as ɵNgClassImplProvider__POST_R3__, NgClassR2Impl as ɵNgClassR2Impl} from './directives/ng_class_impl'; -export {ngStyleDirectiveDef__POST_R3__ as ɵngStyleDirectiveDef__POST_R3__, ngStyleFactoryDef__POST_R3__ as ɵngStyleFactoryDef__POST_R3__} from './directives/ng_style'; -export {NgStyleImpl as ɵNgStyleImpl, NgStyleImplProvider__POST_R3__ as ɵNgStyleImplProvider__POST_R3__, NgStyleR2Impl as ɵNgStyleR2Impl} from './directives/ng_style_impl'; export {DomAdapter as ɵDomAdapter, getDOM as ɵgetDOM, setRootDomAdapter as ɵsetRootDomAdapter} from './dom_adapter'; export {BrowserPlatformLocation as ɵBrowserPlatformLocation} from './location/platform_location'; \ No newline at end of file diff --git a/packages/common/test/directives/styling_differ_spec.ts b/packages/common/test/directives/styling_differ_spec.ts deleted file mode 100644 index a4745b02f4..0000000000 --- a/packages/common/test/directives/styling_differ_spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {StylingDiffer, StylingDifferOptions} from '@angular/common/src/directives/styling_differ'; - -describe('StylingDiffer', () => { - it('should create a key/value object of values from a string', () => { - const d = new StylingDiffer( - 'ngClass', StylingDifferOptions.ForceAsMap | StylingDifferOptions.AllowStringValue); - expect(d.value).toEqual(null); - - d.setInput('one two'); - expect(d.value).toEqual({one: true, two: true}); - - d.setInput('three'); - expect(d.value).toEqual({three: true}); - }); - - - describe('setInput', () => { - - it('should not emit that a value has changed if a new non-collection value was not set', () => { - const d = new StylingDiffer( - 'ngClass', StylingDifferOptions.ForceAsMap | StylingDifferOptions.AllowStringValue); - expect(d.value).toEqual(null); - - d.setInput('one two'); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({one: true, two: true}); - expect(d.updateValue()).toBeFalsy(); - expect(d.value).toEqual({one: true, two: true}); - - d.setInput('three'); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({three: true}); - expect(d.updateValue()).toBeFalsy(); - expect(d.value).toEqual({three: true}); - - d.setInput(null); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual(null); - expect(d.updateValue()).toBeFalsy(); - expect(d.value).toEqual(null); - }); - }); - - - describe('updateValue', () => { - - it('should update the differ value if the contents of a input StringMap change', () => { - const d = new StylingDiffer('ngClass', StylingDifferOptions.ForceAsMap); - - const myMap: {[key: string]: true} = {}; - myMap['abc'] = true; - - d.setInput(myMap); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true}); - expect(d.updateValue()).toBeFalsy(); - - myMap['def'] = true; - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true, def: true}); - expect(d.updateValue()).toBeFalsy(); - - delete myMap['abc']; - delete myMap['def']; - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({}); - expect(d.updateValue()).toBeFalsy(); - }); - - - it('should update the differ value if the contents of an input Array change', () => { - const d = new StylingDiffer('ngClass', StylingDifferOptions.ForceAsMap); - - const myArray: string[] = []; - myArray.push('abc'); - - d.setInput(myArray); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true}); - expect(d.updateValue()).toBeFalsy(); - - myArray.push('def'); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true, def: true}); - expect(d.updateValue()).toBeFalsy(); - - myArray.length = 0; - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({}); - expect(d.updateValue()).toBeFalsy(); - }); - - - it('should update the differ value if the contents of an input Set change', () => { - const d = new StylingDiffer('ngClass', StylingDifferOptions.ForceAsMap); - - const mySet = new Set(); - mySet.add('abc'); - - d.setInput(mySet); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true}); - expect(d.updateValue()).toBeFalsy(); - - mySet.add('def'); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({abc: true, def: true}); - expect(d.updateValue()).toBeFalsy(); - - mySet.clear(); - expect(d.updateValue()).toBeTruthy(); - expect(d.value).toEqual({}); - expect(d.updateValue()).toBeFalsy(); - }); - }); -}); diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index b54f851051..f3fefc1e14 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -288,21 +288,23 @@ runInEachFileSystem(() => { propertiesToConsider: ['esm2015'] }); - // In `@angular/common` the `NgClassR3Impl` class gets exported as something like + // In `@angular/common` the `BrowserPlatformLocation` class gets exported as something like // `ɵangular_packages_common_common_a`. const jsContents = fs.readFile(_(`/node_modules/@angular/common/fesm2015/common.js`)); - const exportedNameMatch = jsContents.match(/export.* NgClassR3Impl as ([^ ,}]+)/); + const exportedNameMatch = + jsContents.match(/export.* BrowserPlatformLocation as ([^ ,}]+)/); if (exportedNameMatch === null) { return fail( - 'Expected `/node_modules/@angular/common/fesm2015/common.js` to export `NgClassR3Impl` via an alias'); + 'Expected `/node_modules/@angular/common/fesm2015/common.js` to export `BrowserPlatformLocation` via an alias'); } const exportedName = exportedNameMatch[1]; // We need to make sure that the flat typings file exports this directly const dtsContents = fs.readFile(_('/node_modules/@angular/common/common.d.ts')); - expect(dtsContents).toContain(`export declare class ${exportedName} extends ɵNgClassImpl`); + expect(dtsContents) + .toContain(`export declare class ${exportedName} extends PlatformLocation`); // And that ngcc's modifications to that class use the correct (exported) name - expect(dtsContents).toContain(`static ɵprov: ɵngcc0.ɵɵInjectableDef<${exportedName}>`); + expect(dtsContents).toContain(`static ɵfac: ɵngcc0.ɵɵFactoryDef<${exportedName}>`); }); it('should add generic type for ModuleWithProviders and generate exports for private modules', diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index c9e55e66a1..5b51fd0478 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -12,19 +12,18 @@ import {Type} from '../core'; import {Injector} from '../di/injector'; import {Sanitizer} from '../sanitization/sanitizer'; import {assertDataInRange} from '../util/assert'; - import {assertComponentType} from './assert'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {registerPostOrderHooks} from './hooks'; -import {CLEAN_PROMISE, addHostBindingsToExpandoInstructions, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderInitialStyling, renderView} from './instructions/shared'; -import {registerInitialStylingOnTNode} from './instructions/styling'; +import {CLEAN_PROMISE, addHostBindingsToExpandoInstructions, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, growHostVarsSpace, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared'; import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'; import {TElementNode, TNode, TNodeType} from './interfaces/node'; import {PlayerHandler} from './interfaces/player'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view'; import {enterView, getPreviousOrParentTNode, incrementActiveDirectiveId, leaveView, setActiveHostElement} from './state'; +import {computeStaticStyling} from './styling/static_styling'; import {setUpAttributes} from './util/attrs_utils'; import {publishDefaultGlobalUtils} from './util/global_utils'; import {defaultScheduler, stringifyForError} from './util/misc_utils'; @@ -175,10 +174,9 @@ export function createRootComponentView( const tNode: TElementNode = getOrCreateTNode(tView, null, 0, TNodeType.Element, null, null); const mergedAttrs = tNode.mergedAttrs = def.hostAttrs; if (mergedAttrs !== null) { - registerInitialStylingOnTNode(tNode, mergedAttrs, 0); + computeStaticStyling(tNode, mergedAttrs); if (rNode !== null) { setUpAttributes(renderer, rNode, mergedAttrs); - renderInitialStyling(renderer, rNode, tNode, false); } } const componentView = createLView( diff --git a/tools/public_api_guard/common/common.d.ts b/tools/public_api_guard/common/common.d.ts index 44da9129dc..14235e9cc7 100644 --- a/tools/public_api_guard/common/common.d.ts +++ b/tools/public_api_guard/common/common.d.ts @@ -189,25 +189,15 @@ export declare class LowerCasePipe implements PipeTransform { transform(value: string): string; } -export declare class NgClass extends NgClassBase implements DoCheck { +export declare class NgClass implements DoCheck { set klass(value: string); set ngClass(value: string | string[] | Set | { [klass: string]: any; }); - constructor(delegate: NgClassImpl); + constructor(_iterableDiffers: IterableDiffers, _keyValueDiffers: KeyValueDiffers, _ngEl: ElementRef, _renderer: Renderer2); ngDoCheck(): void; } -export declare class NgClassBase { - protected _delegate: NgClassImpl; - constructor(_delegate: NgClassImpl); - getValue(): { - [key: string]: any; - } | null; - static ɵdir: any; - static ɵfac: any; -} - export declare class NgComponentOutlet implements OnChanges, OnDestroy { ngComponentOutlet: Type; ngComponentOutletContent: any[][]; @@ -275,24 +265,14 @@ export declare class NgPluralCase { constructor(value: string, template: TemplateRef, viewContainer: ViewContainerRef, ngPlural: NgPlural); } -export declare class NgStyle extends NgStyleBase implements DoCheck { - set ngStyle(value: { +export declare class NgStyle implements DoCheck { + set ngStyle(values: { [klass: string]: any; } | null); - constructor(delegate: NgStyleImpl); + constructor(_ngEl: ElementRef, _differs: KeyValueDiffers, _renderer: Renderer2); ngDoCheck(): void; } -export declare class NgStyleBase { - protected _delegate: NgStyleImpl; - constructor(_delegate: NgStyleImpl); - getValue(): { - [key: string]: any; - } | null; - static ɵdir: any; - static ɵfac: any; -} - export declare class NgSwitch { set ngSwitch(newValue: any); }