fix(ivy): ensure falsy styling is not applied during creation mode (#26793)

PR Close #26793
This commit is contained in:
Matias Niemelä
2018-10-26 16:32:18 -07:00
parent 332394d87c
commit 68b2211e64
6 changed files with 235 additions and 88 deletions

View File

@ -1161,8 +1161,9 @@ export function elementStyling(
*/
export function elementStylingApply(index: number): void {
const viewData = getViewData();
const totalPlayersQueued =
renderStyleAndClassBindings(getStylingContext(index, viewData), getRenderer(), viewData);
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
const totalPlayersQueued = renderStyleAndClassBindings(
getStylingContext(index, viewData), getRenderer(), viewData, isFirstRender);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(viewData);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);

View File

@ -21,8 +21,8 @@ export interface Player {
export const enum BindingType {
Unset = 0,
Class = 2,
Style = 3,
Class = 1,
Style = 2,
}
export interface BindingStore { setValue(prop: string, value: any): void; }
@ -35,7 +35,7 @@ export interface BindingStore { setValue(prop: string, value: any): void; }
* to be used with `PlayerFactory`.
*/
export interface PlayerFactoryBuildFn {
(element: HTMLElement, type: BindingType, values: {[key: string]: any},
(element: HTMLElement, type: BindingType, values: {[key: string]: any}, isFirstRender: boolean,
currentPlayer: Player|null): Player|null;
}
@ -53,7 +53,7 @@ export interface PlayerFactoryBuildFn {
export interface PlayerFactory { '__brand__': 'Brand for PlayerFactory that nothing will match'; }
export interface PlayerBuilder extends BindingStore {
buildPlayer(currentPlayer: Player|null): Player|undefined|null;
buildPlayer(currentPlayer: Player|null, isFirstRender: boolean): Player|undefined|null;
}
/**

View File

@ -196,7 +196,7 @@ export const enum StylingFlags {
// The max amount of bits used to represent these configuration values
BitCountSize = 5,
// There are only five bits here
BitMask = 0b1111
BitMask = 0b11111
}
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */

View File

@ -480,8 +480,10 @@ export function updateClassProp(
*/
export function renderStyleAndClassBindings(
context: StylingContext, renderer: Renderer3, rootOrView: RootContext | LViewData,
classesStore?: BindingStore | null, stylesStore?: BindingStore | null): number {
isFirstRender: boolean, classesStore?: BindingStore | null,
stylesStore?: BindingStore | null): number {
let totalPlayersQueued = 0;
if (isContextDirty(context)) {
const flushPlayerBuilders: any =
context[StylingIndex.MasterFlagPosition] & StylingFlags.PlayerBuildersDirty;
@ -523,15 +525,23 @@ export function renderStyleAndClassBindings(
valueToApply = getInitialValue(context, flag);
}
if (isClassBased) {
setClass(
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
} else {
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
setStyle(
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
playerBuilder);
// if the first render is true then we do not want to start applying falsy
// values to the DOM element's styling. Otherwise then we know there has
// been a change and even if it's falsy then it's removing something that
// was truthy before.
const doApplyValue = isFirstRender ? valueToApply : true;
if (doApplyValue) {
if (isClassBased) {
setClass(
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
} else {
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
setStyle(
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
playerBuilder);
}
}
setDirty(context, i, false);
}
}
@ -547,7 +557,7 @@ export function renderStyleAndClassBindings(
const playerInsertionIndex = i + PlayerIndex.PlayerOffsetPosition;
const oldPlayer = playerContext[playerInsertionIndex] as Player | null;
if (builder) {
const player = builder.buildPlayer(oldPlayer);
const player = builder.buildPlayer(oldPlayer, isFirstRender);
if (player !== undefined) {
if (player != null) {
const wasQueued = addPlayerInternal(
@ -924,13 +934,13 @@ export class ClassAndStylePlayerBuilder<T> implements PlayerBuilder {
}
}
buildPlayer(currentPlayer?: Player|null): Player|undefined|null {
buildPlayer(currentPlayer: Player|null, isFirstRender: boolean): Player|undefined|null {
// if no values have been set here then this means the binding didn't
// change and therefore the binding values were not updated through
// `setValue` which means no new player will be provided.
if (this._dirty) {
const player =
this._factory.fn(this._element, this._type, this._values !, currentPlayer || null);
const player = this._factory.fn(
this._element, this._type, this._values !, isFirstRender, currentPlayer || null);
this._values = {};
this._dirty = false;
return player;