Skip to content

Commit a5c4bb5

Browse files
matskoalxhub
authored andcommitted
fix(animations): make sure @.disabled works in non-animation components
Note 4.3 only! Prior to this fix when [@.disabled] was used in a component that contained zero animation code it wouldn't register properly because the renderer associated with that component was not an animation renderer. This patch ensures that it gets registered even when there are no animations set.
1 parent 4c1f32b commit a5c4bb5

File tree

3 files changed

+84
-20
lines changed

3 files changed

+84
-20
lines changed

packages/animations/browser/src/render/animation_engine_next.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,17 @@ export class AnimationEngine {
6767
this._transitionEngine.removeNode(namespaceId, element, context);
6868
}
6969

70-
process(namespaceId: string, element: any, property: string, value: any): boolean {
71-
switch (property.charAt(0)) {
72-
case '.':
73-
if (property == '.disabled') {
74-
this._transitionEngine.markElementAsDisabled(element, !!value);
75-
}
76-
return false;
77-
case '@':
78-
const [id, action] = parseTimelineCommand(property);
79-
const args = value as any[];
80-
this._timelineEngine.command(id, element, action, args);
81-
return false;
82-
default:
83-
return this._transitionEngine.trigger(namespaceId, element, property, value);
70+
disableAnimations(element: any, disable: boolean) {
71+
this._transitionEngine.markElementAsDisabled(element, disable);
72+
}
73+
74+
process(namespaceId: string, element: any, property: string, value: any) {
75+
if (property.charAt(0) == '@') {
76+
const [id, action] = parseTimelineCommand(property);
77+
const args = value as any[];
78+
this._timelineEngine.command(id, element, action, args);
79+
} else {
80+
this._transitionEngine.trigger(namespaceId, element, property, value);
8481
}
8582
}
8683

packages/core/test/animation/animation_integration_spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,57 @@ export function main() {
22812281
expect(cmp.startEvent.totalTime).toEqual(9876);
22822282
// the done event isn't fired because it's an actual animation
22832283
}));
2284+
2285+
it('should work when there are no animations on the component handling the disable/enable flag',
2286+
() => {
2287+
@Component({
2288+
selector: 'parent-cmp',
2289+
template: `
2290+
<div [@.disabled]="disableExp">
2291+
<child-cmp #child></child-cmp>
2292+
</div>
2293+
`
2294+
})
2295+
class ParentCmp {
2296+
@ViewChild('child') public child: ChildCmp|null = null;
2297+
disableExp = false;
2298+
}
2299+
2300+
@Component({
2301+
selector: 'child-cmp',
2302+
template: `
2303+
<div [@myAnimation]="exp"></div>
2304+
`,
2305+
animations: [trigger(
2306+
'myAnimation',
2307+
[transition(
2308+
'* => go, * => goAgain',
2309+
[style({opacity: 0}), animate('1s', style({opacity: 1}))])])]
2310+
})
2311+
class ChildCmp {
2312+
public exp = '';
2313+
}
2314+
2315+
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
2316+
2317+
const fixture = TestBed.createComponent(ParentCmp);
2318+
const cmp = fixture.componentInstance;
2319+
cmp.disableExp = true;
2320+
fixture.detectChanges();
2321+
resetLog();
2322+
2323+
const child = cmp.child !;
2324+
child.exp = 'go';
2325+
fixture.detectChanges();
2326+
2327+
expect(getLog().length).toEqual(0);
2328+
resetLog();
2329+
2330+
cmp.disableExp = false;
2331+
child.exp = 'goAgain';
2332+
fixture.detectChanges();
2333+
expect(getLog().length).toEqual(1);
2334+
});
22842335
});
22852336
});
22862337

packages/platform-browser/animations/src/animation_renderer.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import {AnimationTriggerMetadata} from '@angular/animations';
99
import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser';
1010
import {Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core';
1111

12+
const ANIMATION_PREFIX = '@';
13+
const DISABLE_ANIMATIONS_FLAG = '@.disabled';
14+
1215
@Injectable()
1316
export class AnimationRendererFactory implements RendererFactory2 {
1417
private _currentId: number = 0;
@@ -174,14 +177,22 @@ export class BaseAnimationRenderer implements Renderer2 {
174177
}
175178

176179
setProperty(el: any, name: string, value: any): void {
177-
this.delegate.setProperty(el, name, value);
180+
if (name.charAt(0) == ANIMATION_PREFIX && name == DISABLE_ANIMATIONS_FLAG) {
181+
this.disableAnimations(el, !!value);
182+
} else {
183+
this.delegate.setProperty(el, name, value);
184+
}
178185
}
179186

180187
setValue(node: any, value: string): void { this.delegate.setValue(node, value); }
181188

182189
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void {
183190
return this.delegate.listen(target, eventName, callback);
184191
}
192+
193+
protected disableAnimations(element: any, value: boolean) {
194+
this.engine.disableAnimations(element, value);
195+
}
185196
}
186197

187198
export class AnimationRenderer extends BaseAnimationRenderer implements Renderer2 {
@@ -193,21 +204,26 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer
193204
}
194205

195206
setProperty(el: any, name: string, value: any): void {
196-
if (name.charAt(0) == '@') {
197-
name = name.substr(1);
198-
this.engine.process(this.namespaceId, el, name, value);
207+
if (name.charAt(0) == ANIMATION_PREFIX) {
208+
if (name.charAt(1) == '.' && name == DISABLE_ANIMATIONS_FLAG) {
209+
this.disableAnimations(el, !!value);
210+
} else {
211+
this.engine.process(this.namespaceId, el, name.substr(1), value);
212+
}
199213
} else {
200214
this.delegate.setProperty(el, name, value);
201215
}
202216
}
203217

204218
listen(target: 'window'|'document'|'body'|any, eventName: string, callback: (event: any) => any):
205219
() => void {
206-
if (eventName.charAt(0) == '@') {
220+
if (eventName.charAt(0) == ANIMATION_PREFIX) {
207221
const element = resolveElementFromTarget(target);
208222
let name = eventName.substr(1);
209223
let phase = '';
210-
if (name.charAt(0) != '@') { // transition-specific
224+
// @listener.phase is for trigger animation callbacks
225+
// @@listener is for animation builder callbacks
226+
if (name.charAt(0) != ANIMATION_PREFIX) {
211227
[name, phase] = parseTriggerCallbackName(name);
212228
}
213229
return this.engine.listen(this.namespaceId, element, name, phase, event => {

0 commit comments

Comments
 (0)