refactor(ivy): remove styling state storage and introduce direct style writing (#32591)

This patch is a final major refactor in styling Angular.

This PR includes three main fixes:

All temporary state taht is persisted between template style/class application
and style/class application in host bindings is now removed.
Removes the styling() and stylingApply() instructions.
Introduces a "direct apply" mode that is used apply prop-based
style/class in the event that there are no map-based bindings as
well as property collisions.

PR Close #32259

PR Close #32591
This commit is contained in:
Matias Niemelä
2019-09-09 13:14:26 -07:00
committed by Andrew Kushnir
parent e6ed4a21e4
commit 4f41473048
46 changed files with 1756 additions and 1614 deletions

View File

@ -69,8 +69,6 @@ export class Identifiers {
static elementContainer: o.ExternalReference = {name: 'ɵɵelementContainer', moduleName: CORE};
static styling: o.ExternalReference = {name: 'ɵɵstyling', moduleName: CORE};
static styleMap: o.ExternalReference = {name: 'ɵɵstyleMap', moduleName: CORE};
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
@ -115,8 +113,6 @@ export class Identifiers {
static stylePropInterpolateV:
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
static stylingApply: o.ExternalReference = {name: 'ɵɵstylingApply', moduleName: CORE};
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE};

View File

@ -713,16 +713,6 @@ function createHostBindingsFunction(
}
if (styleBuilder.hasBindings) {
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
// MUST be registered on a given element within the component/directive
// templateFn/hostBindingsFn functions. The instruction below will figure out
// what all the bindings are and then generate the statements required to register
// those bindings to the element via `styling`.
const stylingInstruction = styleBuilder.buildStylingInstruction(null, constantPool);
if (stylingInstruction) {
createStatements.push(createStylingStmt(stylingInstruction, bindingContext, bindingFn));
}
// finally each binding that was registered in the statement above will need to be added to
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
// are evaluated and updated for the element.

View File

@ -7,7 +7,7 @@
*/
import {ConstantPool} from '../../constant_pool';
import {AttributeMarker} from '../../core';
import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
import {AST, ASTWithSource, BindingPipe, BindingType, Interpolation} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import {isEmptyExpression} from '../../template_parser/template_parser';
@ -68,7 +68,6 @@ interface BoundStylingEntry {
* classMap(...)
* styleProp(...)
* classProp(...)
* stylingApply(...)
* }
*
* The creation/update methods within the builder class produce these instructions.
@ -81,6 +80,7 @@ export class StylingBuilder {
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
*/
public hasBindings = false;
public hasBindingsWithPipes = false;
/** the input for [class] (if it exists) */
private _classMapInput: BoundStylingEntry|null = null;
@ -187,6 +187,7 @@ export class StylingBuilder {
}
this._lastStylingInput = entry;
this._firstStylingInput = this._firstStylingInput || entry;
this._checkForPipes(value);
this.hasBindings = true;
return entry;
}
@ -207,10 +208,17 @@ export class StylingBuilder {
}
this._lastStylingInput = entry;
this._firstStylingInput = this._firstStylingInput || entry;
this._checkForPipes(value);
this.hasBindings = true;
return entry;
}
private _checkForPipes(value: AST) {
if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
this.hasBindingsWithPipes = true;
}
}
/**
* Registers the element's static style string value to the builder.
*
@ -284,25 +292,6 @@ export class StylingBuilder {
return null;
}
/**
* Builds an instruction with all the expressions and parameters for `styling`.
*
* The instruction generation code below is used for producing the AOT statement code which is
* responsible for registering style/class bindings to an element.
*/
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
StylingInstruction|null {
if (this.hasBindings) {
return {
sourceSpan,
allocateBindingSlots: 0,
reference: R3.styling,
params: () => [],
};
}
return null;
}
/**
* Builds an instruction with all the expressions and parameters for `classMap`.
*
@ -422,15 +411,6 @@ export class StylingBuilder {
return [];
}
private _buildApplyFn(): StylingInstruction {
return {
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
reference: R3.stylingApply,
allocateBindingSlots: 0,
params: () => { return []; }
};
}
private _buildSanitizerFn(): StylingInstruction {
return {
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
@ -460,7 +440,6 @@ export class StylingBuilder {
}
instructions.push(...this._buildStyleInputs(valueConverter));
instructions.push(...this._buildClassInputs(valueConverter));
instructions.push(this._buildApplyFn());
}
return instructions;
}

View File

@ -623,11 +623,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
element.children.length > 0;
const createSelfClosingInstruction = !stylingBuilder.hasBindings &&
const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren;
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
!stylingBuilder.hasBindings && hasTextChildrenOnly(element.children);
const createSelfClosingI18nInstruction =
!createSelfClosingInstruction && hasTextChildrenOnly(element.children);
if (createSelfClosingInstruction) {
this.creationInstruction(
@ -681,16 +680,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
}
// The style bindings code is placed into two distinct blocks within the template function AOT
// code: creation and update. The creation code contains the `styling` instructions
// which will apply the collected binding values to the element. `styling` is
// designed to run inside of `elementStart` and `elementEnd`. The update instructions
// (things like `styleProp`, `classProp`, etc..) are applied later on in this
// file
this.processStylingInstruction(
elementIndex,
stylingBuilder.buildStylingInstruction(element.sourceSpan, this.constantPool), true);
// Generate Listeners (outputs)
element.outputs.forEach((outputAst: t.BoundEvent) => {
this.creationInstruction(