Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integration/_payload-limits.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 194626,
"main": 207765,
"polyfills": 38390
}
}
Expand Down
7 changes: 6 additions & 1 deletion packages/common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ 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, getLocaleCurrencyName, getLocaleCurrencySymbol} from './i18n/locale_data_api';
export {parseCookieValue as ɵparseCookieValue} from './cookie';
export {CommonModule, DeprecatedI18NPipesModule} from './common_module';
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
export {NgClass, NgClassBase, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgStyleBase, 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 {DeprecatedDatePipe, DeprecatedCurrencyPipe, DeprecatedDecimalPipe, DeprecatedPercentPipe} from './pipes/deprecated/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';
export {VERSION} from './version';
export {ViewportScroller, NullViewportScroller as ɵNullViewportScroller} from './viewport_scroller';

export {NgClassImplProvider__POST_R3__ as ɵNgClassImplProvider__POST_R3__, NgClassR2Impl as ɵNgClassR2Impl, NgClassImpl as ɵNgClassImpl} from './directives/ng_class_impl';
export {NgStyleImplProvider__POST_R3__ as ɵNgStyleImplProvider__POST_R3__, NgStyleR2Impl as ɵNgStyleR2Impl, NgStyleImpl as ɵNgStyleImpl} from './directives/ng_style_impl';
export {ngStyleDirectiveDef__POST_R3__ as ɵngStyleDirectiveDef__POST_R3__} from './directives/ng_style';
export {ngClassDirectiveDef__POST_R3__ as ɵngClassDirectiveDef__POST_R3__} from './directives/ng_class';
9 changes: 5 additions & 4 deletions packages/common/src/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
*/

import {Provider} from '@angular/core';

import {NgClass} from './ng_class';
import {NgClass, NgClassBase} 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} from './ng_style';
import {NgStyle, NgStyleBase} from './ng_style';
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
import {NgTemplateOutlet} from './ng_template_outlet';

export {
NgClass,
NgClassBase,
NgComponentOutlet,
NgForOf,
NgForOfContext,
Expand All @@ -27,10 +27,11 @@ export {
NgPlural,
NgPluralCase,
NgStyle,
NgStyleBase,
NgSwitch,
NgSwitchCase,
NgSwitchDefault,
NgTemplateOutlet
NgTemplateOutlet,
};


Expand Down
185 changes: 68 additions & 117 deletions packages/common/src/directives/ng_class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,68 @@
* 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, ɵelementStyling, ɵelementStylingApply, ɵelementStylingMap} 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,
factory: () => {},
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
if (rf & ɵRenderFlags.Create) {
ɵelementStyling(null, null, null, ctx);
}
if (rf & ɵRenderFlags.Update) {
ɵelementStylingMap(elIndex, ctx.getValue(), null, ctx);
ɵelementStylingApply(elIndex, ctx);
}
}
});

export const ngClassDirectiveDef = ngClassDirectiveDef__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
* `ngDirectiveDef` property on the class).
*
* Note that the `ngDirectiveDef` 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 ngDirectiveDef: any = ngClassDirectiveDef;

constructor(protected _delegate: NgClassImpl) {}

getValue() { return this._delegate.getValue(); }
}

/**
* @ngModule CommonModule
Expand Down Expand Up @@ -36,126 +96,17 @@ import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer,
*
* @publicApi
*/
@Directive({selector: '[ngClass]'})
export class NgClass implements DoCheck {
// TODO(issue/24571): remove '!'.
private _iterableDiffer !: IterableDiffer<string>| null;
// TODO(issue/24571): remove '!'.
private _keyValueDiffer !: KeyValueDiffer<string, any>| null;
private _initialClasses: string[] = [];
// TODO(issue/24571): remove '!'.
private _rawClass !: string[] | Set<string>| {[klass: string]: any};

constructor(
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Directive({selector: '[ngClass]', providers: [NgClassImplProvider]})
export class NgClass extends NgClassBase implements DoCheck {
constructor(delegate: NgClassImpl) { super(delegate); }

@Input('class')
set klass(value: string) {
this._removeClasses(this._initialClasses);
this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : [];
this._applyClasses(this._initialClasses);
this._applyClasses(this._rawClass);
}
set klass(value: string) { this._delegate.setClass(value); }

@Input()
@Input('ngClass')
set ngClass(value: string|string[]|Set<string>|{[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();
}
}
}

ngDoCheck(): void {
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<string, any>): 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);
}
});
this._delegate.setNgClass(value);
}

private _applyIterableChanges(changes: IterableChanges<string>): 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<string>|{[klass: string]: any}) {
if (rawClassVal) {
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
(<any>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<string>|{[klass: string]: any}) {
if (rawClassVal) {
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
(<any>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);
}
});
}
}
ngDoCheck() { this._delegate.applyChanges(); }
}
Loading