fix(animations): properly clean up queried element styles in safari/edge (#23633)

Prior to this patch, if an element is queried and animated for 0 seconds
(just a style() call and nothing else) then the styles applied would not
be properly cleaned up due to their camelCased nature.

PR Close #23633
This commit is contained in:
Matias Niemelä 2018-05-01 10:40:11 -07:00 committed by Igor Minar
parent 2cf6244b1d
commit da9ff255dd
4 changed files with 74 additions and 13 deletions

View File

@ -9,7 +9,7 @@ import {AnimationPlayer, ɵStyleData} from '@angular/animations';
import {allowPreviousPlayerStylesMerge, balancePreviousStylesIntoKeyframes, computeStyle} from '../../util';
import {AnimationDriver} from '../animation_driver';
import {containsElement, invokeQuery, matchesElement, validateStyleProperty} from '../shared';
import {containsElement, hypenatePropsObject, invokeQuery, matchesElement, validateStyleProperty} from '../shared';
import {CssKeyframesPlayer} from './css_keyframes_player';
import {DirectStylePlayer} from './direct_style_player';
@ -137,15 +137,6 @@ function flattenKeyframesIntoStyles(
return flatKeyframes;
}
function hypenatePropsObject(object: {[key: string]: any}): {[key: string]: any} {
const newObj: {[key: string]: any} = {};
Object.keys(object).forEach(prop => {
const newProp = prop.replace(/([a-z])([A-Z])/g, '$1-$2');
newObj[newProp] = object[prop];
});
return newObj;
}
function removeElement(node: any) {
node.parentNode.removeChild(node);
}

View File

@ -6,12 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NoopAnimationPlayer} from '@angular/animations';
import {hypenatePropsObject} from '../shared';
export class DirectStylePlayer extends NoopAnimationPlayer {
private _startingStyles: {[key: string]: any}|null = {};
private __initialized = false;
private _styles: {[key: string]: any};
constructor(public element: any, private _styles: {[key: string]: any}) { super(); }
constructor(public element: any, styles: {[key: string]: any}) {
super();
this._styles = hypenatePropsObject(styles);
}
init() {
if (this.__initialized || !this._startingStyles) return;
@ -25,7 +30,8 @@ export class DirectStylePlayer extends NoopAnimationPlayer {
play() {
if (!this._startingStyles) return;
this.init();
Object.keys(this._styles).forEach(prop => { this.element.style[prop] = this._styles[prop]; });
Object.keys(this._styles)
.forEach(prop => this.element.style.setProperty(prop, this._styles[prop]));
super.play();
}
@ -34,7 +40,7 @@ export class DirectStylePlayer extends NoopAnimationPlayer {
Object.keys(this._startingStyles).forEach(prop => {
const value = this._startingStyles ![prop];
if (value) {
this.element.style[prop] = value;
this.element.style.setProperty(prop, value);
} else {
this.element.style.removeProperty(prop);
}

View File

@ -203,3 +203,12 @@ export function getBodyNode(): any|null {
export const matchesElement = _matches;
export const containsElement = _contains;
export const invokeQuery = _query;
export function hypenatePropsObject(object: {[key: string]: any}): {[key: string]: any} {
const newObj: {[key: string]: any} = {};
Object.keys(object).forEach(prop => {
const newProp = prop.replace(/([a-z])([A-Z])/g, '$1-$2');
newObj[newProp] = object[prop];
});
return newObj;
}

View File

@ -248,6 +248,61 @@ import {TestBed} from '../../testing';
assertStyle(element, 'width', '200px');
assertStyle(element, 'height', '50px');
});
it('should clean up 0 second animation styles (queried styles) that contain camel casing when complete',
() => {
@Component({
selector: 'ani-cmp',
template: `
<div #elm [@myAnimation]="myAnimationExp">
<div class="foo"></div>
<div class="bar"></div>
</div>
`,
animations: [
trigger(
'myAnimation',
[
state('go', style({width: '200px'})),
transition(
'* => go',
[
query('.foo', [style({maxHeight: '0px'})]),
query(
'.bar',
[
style({width: '0px'}),
animate('1s', style({width: '100px'})),
]),
]),
]),
]
})
class Cmp {
@ViewChild('elm') public element: any;
public myAnimationExp = '';
}
TestBed.configureTestingModule({declarations: [Cmp]});
const engine = TestBed.get(AnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
const elm = cmp.element.nativeElement;
const foo = elm.querySelector('.foo') as HTMLElement;
cmp.myAnimationExp = 'go';
fixture.detectChanges();
expect(foo.style.getPropertyValue('max-height')).toEqual('0px');
const player = engine.players.pop();
player.finish();
expect(foo.style.getPropertyValue('max-height')).toBeFalsy();
});
});
})();