fix(ngClass): do not deconstruct classes on element removal (#10303)

Prior to this fix [ngClass] would remove all dynamic classes
when destroyed. It's essential that classes are persisted such
that remove-based animations will still be stylistically correct.
This patch fixes this issue.

Closes #10008
Closes #10303
This commit is contained in:
Matias Niemelä 2016-07-26 15:20:27 -07:00 committed by GitHub
parent 62e7c0f464
commit ba88db5141
3 changed files with 36 additions and 6 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, OnDestroy, Renderer} from '@angular/core';
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
import {isArray, isPresent, isString} from '../facade/lang';
@ -75,7 +75,7 @@ import {isArray, isPresent, isString} from '../facade/lang';
* @stable
*/
@Directive({selector: '[ngClass]'})
export class NgClass implements DoCheck, OnDestroy {
export class NgClass implements DoCheck {
private _iterableDiffer: IterableDiffer;
private _keyValueDiffer: KeyValueDiffer;
private _initialClasses: string[] = [];
@ -129,8 +129,6 @@ export class NgClass implements DoCheck, OnDestroy {
}
}
ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[key: string]: any}): void {
this._applyClasses(rawClassVal, true);
this._applyInitialClasses(false);

View File

@ -874,6 +874,39 @@ function declareTests({useJit}: {useJit: boolean}) {
})));
});
describe('ng directives', () => {
describe('[ngClass]', () => {
it('should persist ngClass class values when a remove element animation is active',
inject(
[TestComponentBuilder, AnimationDriver],
fakeAsync(
(tcb: TestComponentBuilder, driver: InnerContentTrackingAnimationDriver) => {
makeAnimationCmp(
tcb, `<div [ngClass]="exp2" *ngIf="exp" @trigger></div>`,
[
trigger('trigger', [transition('* => void', [animate(1000)])]),
],
(fixture: any /** TODO #9100 */) => {
var cmp = fixture.debugElement.componentInstance;
cmp.exp = true;
cmp.exp2 = 'blue';
fixture.detectChanges();
flushMicrotasks();
expect(driver.log.length).toEqual(0);
cmp.exp = false;
fixture.detectChanges();
flushMicrotasks();
var animation = driver.log.pop();
var element = animation['element'];
(<any>expect(element)).toHaveCssClass('blue');
});
})));
});
});
describe('animation states', () => {
it('should throw an error when an animation is referenced that isn\'t defined within the component annotation',
inject(

View File

@ -291,14 +291,13 @@ export declare const NG_VALIDATORS: OpaqueToken;
export declare const NG_VALUE_ACCESSOR: OpaqueToken;
/** @stable */
export declare class NgClass implements DoCheck, OnDestroy {
export declare class NgClass implements DoCheck {
initialClasses: string;
ngClass: string | string[] | Set<string> | {
[key: string]: any;
};
constructor(_iterableDiffers: IterableDiffers, _keyValueDiffers: KeyValueDiffers, _ngEl: ElementRef, _renderer: Renderer);
ngDoCheck(): void;
ngOnDestroy(): void;
}
/** @experimental */