fix(ivy): introduce host-specific styling instructions (#29292)

This patch is the first of a few patches which separates the
styling logic between template bindings (e.g. <div [style])
from host bindings (e.g. @HostBinding('style')). This patch
in particular introduces a series of host-specific styling
instructions and changes the existing set of template styling
instructions not to accept directives. The underyling code (which
communicates with the styling algorithm) still works as it did
before.

This PR also separates the styling instruction code into a separate
file and moves over all other instructions into an dedicated
instructions directory.

PR Close #29292
This commit is contained in:
Matias Niemelä 2019-03-15 13:45:08 -07:00
parent d5e3f2c64b
commit 8714daf276
23 changed files with 736 additions and 432 deletions

View File

@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 13921,
"main": 14106,
"polyfills": 38390
}
}

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, DoCheck, Input, ɵRenderFlags, ɵdefineDirective, ɵelementStyling, ɵelementStylingApply, ɵelementStylingMap} from '@angular/core';
import {Directive, DoCheck, Input, ɵRenderFlags, ɵdefineDirective, ɵelementHostStyling, ɵelementHostStylingApply, ɵelementHostStylingMap} from '@angular/core';
import {NgClassImpl, NgClassImplProvider} from './ng_class_impl';
@ -35,11 +35,11 @@ export const ngClassDirectiveDef__POST_R3__ = ɵdefineDirective({
factory: () => {},
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
if (rf & ɵRenderFlags.Create) {
ɵelementStyling(null, null, null, ctx);
ɵelementHostStyling();
}
if (rf & ɵRenderFlags.Update) {
ɵelementStylingMap(elIndex, ctx.getValue(), null, ctx);
ɵelementStylingApply(elIndex, ctx);
ɵelementHostStylingMap(ctx.getValue());
ɵelementHostStylingApply();
}
}
});

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, DoCheck, Input, ɵRenderFlags, ɵdefineDirective, ɵelementStyling, ɵelementStylingApply, ɵelementStylingMap} from '@angular/core';
import {Directive, DoCheck, Input, ɵRenderFlags, ɵdefineDirective, ɵelementHostStyling, ɵelementHostStylingApply, ɵelementHostStylingMap} from '@angular/core';
import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl';
@ -35,11 +35,11 @@ export const ngStyleDirectiveDef__POST_R3__ = ɵdefineDirective({
factory: () => {},
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
if (rf & ɵRenderFlags.Create) {
ɵelementStyling(null, null, null, ctx);
ɵelementHostStyling();
}
if (rf & ɵRenderFlags.Update) {
ɵelementStylingMap(elIndex, null, ctx.getValue(), ctx);
ɵelementStylingApply(elIndex, ctx);
ɵelementHostStylingMap(null, ctx.getValue());
ɵelementHostStylingApply();
}
}
});

View File

@ -347,7 +347,7 @@ describe('compiler compliance: bindings', () => {
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementHostAttrs(ctx, $c0$);
$r3$.ɵelementHostAttrs($c0$);
}
}
});
@ -405,7 +405,7 @@ describe('compiler compliance: bindings', () => {
factory: function HostAttributeComp_Factory(t) { return new (t || HostAttributeComp)(); },
hostBindings: function HostAttributeComp_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementHostAttrs(ctx, $c0$);
$r3$.ɵelementHostAttrs($c0$);
}
@ -417,7 +417,7 @@ describe('compiler compliance: bindings', () => {
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementHostAttrs(ctx, $c1$);
$r3$.ɵelementHostAttrs($c1$);
}

View File

@ -985,14 +985,14 @@ describe('compiler compliance: styling', () => {
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementHostAttrs(ctx, $e0_attrs$);
$r3$.ɵelementStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostAttrs($e0_attrs$);
$r3$.ɵelementHostStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵdefaultStyleSanitizer);
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myColorProp, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStylingMap(ctx.myClass, ctx.myStyle);
$r3$.ɵelementHostStyleProp(0, ctx.myColorProp);
$r3$.ɵelementHostClassProp(0, ctx.myFooClass);
$r3$.ɵelementHostStylingApply();
}
},
consts: 0,
@ -1046,15 +1046,15 @@ describe('compiler compliance: styling', () => {
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer);
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClasses, ctx.myStyle, ctx);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeightProp, "pt", ctx);
$r3$.ɵelementStyleProp(elIndex, 1, ctx.myWidthProp, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
$r3$.ɵelementClassProp(elIndex, 1, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStylingMap(ctx.myClasses, ctx.myStyle);
$r3$.ɵelementHostStyleProp(0, ctx.myHeightProp, "pt");
$r3$.ɵelementHostStyleProp(1, ctx.myWidthProp);
$r3$.ɵelementHostClassProp(0, ctx.myBarClass);
$r3$.ɵelementHostClassProp(1, ctx.myFooClass);
$r3$.ɵelementHostStylingApply();
}
},
consts: 0,
@ -1115,7 +1115,7 @@ describe('compiler compliance: styling', () => {
if (rf & 2) {
$r3$.ɵelementStylingMap(0, ctx.myClassExp, ctx.myStyleExp);
$r3$.ɵelementStyleProp(0, 0, ctx.myHeightExp, null, true);
$r3$.ɵelementClassProp(0, 0, ctx.myBarClassExp, null, true);
$r3$.ɵelementClassProp(0, 0, ctx.myBarClassExp, true);
$r3$.ɵelementStylingApply(0);
}
},
@ -1127,13 +1127,13 @@ describe('compiler compliance: styling', () => {
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer);
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClassExp, ctx.myStyleExp, ctx);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidthExp, null, ctx, true);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClassExp, ctx, true);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStylingMap(ctx.myClassExp, ctx.myStyleExp);
$r3$.ɵelementHostStyleProp(0, ctx.myWidthExp, null, true);
$r3$.ɵelementHostClassProp(0, ctx.myFooClassExp, true);
$r3$.ɵelementHostStylingApply();
}
},
`;
@ -1195,33 +1195,33 @@ describe('compiler compliance: styling', () => {
function ClassDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling(null, null, null, ctx);
$r3$.ɵelementHostStyling();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(elIndex, ctx.myClassMap, null, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStylingMap(ctx.myClassMap);
$r3$.ɵelementHostStylingApply();
}
}
function WidthDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling($widthDir_classes$, $widthDir_styles$, null, ctx);
$r3$.ɵelementHostStyling($widthDir_classes$, $widthDir_styles$);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStyleProp(0, ctx.myWidth);
$r3$.ɵelementHostClassProp(0, ctx.myFooClass);
$r3$.ɵelementHostStylingApply();
}
}
function HeightDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵelementStyling($heightDir_classes$, $heightDir_styles$, null, ctx);
$r3$.ɵelementHostStyling($heightDir_classes$, $heightDir_styles$);
}
if (rf & 2) {
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStyleProp(0, ctx.myHeight);
$r3$.ɵelementHostClassProp(0, ctx.myBarClass);
$r3$.ɵelementHostStylingApply();
}
}
@ -1276,14 +1276,14 @@ describe('compiler compliance: styling', () => {
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵallocHostVars(2);
$r3$.ɵelementHostAttrs(ctx, $_c0$);
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer, ctx);
$r3$.ɵelementHostAttrs($_c0$);
$r3$.ɵelementHostStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
}
if (rf & 2) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);
$r3$.ɵelementProperty(elIndex, "title", $r3$.ɵbind(ctx.title), null, true);
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStylingMap(ctx.myClass, ctx.myStyle);
$r3$.ɵelementHostStylingApply();
}
}
`;
@ -1323,14 +1323,14 @@ describe('compiler compliance: styling', () => {
hostBindings: function WidthDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 1) {
$r3$.ɵallocHostVars(2);
$r3$.ɵelementStyling($_c0$, $_c1$, null, ctx);
$r3$.ɵelementHostStyling($_c0$, $_c1$);
}
if (rf & 2) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id), null, true);
$r3$.ɵelementProperty(elIndex, "title", $r3$.ɵbind(ctx.title), null, true);
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
$r3$.ɵelementStylingApply(elIndex, ctx);
$r3$.ɵelementHostStyleProp(0, ctx.myWidth);
$r3$.ɵelementHostClassProp(0, ctx.myFooClass);
$r3$.ɵelementHostStylingApply();
}
}
`;

View File

@ -1594,13 +1594,13 @@ describe('ngtsc behavioral tests', () => {
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); });
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onBodyClick($event); }, false, i0.ɵresolveBody);
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
i0.ɵelementStyling(_c0, null, null, ctx);
i0.ɵelementHostStyling(_c0);
}
if (rf & 2) {
i0.ɵelementAttribute(elIndex, "hello", i0.ɵbind(ctx.foo));
i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(ctx.bar), null, true);
i0.ɵelementClassProp(elIndex, 0, ctx.someClass, ctx);
i0.ɵelementStylingApply(elIndex, ctx);
i0.ɵelementHostClassProp(0, ctx.someClass);
i0.ɵelementHostStylingApply();
}
}
`;
@ -1629,7 +1629,7 @@ describe('ngtsc behavioral tests', () => {
`);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('i0.ɵelementHostAttrs(ctx, ["test", test])');
expect(jsContents).toContain('i0.ɵelementHostAttrs(["test", test])');
});
it('should accept enum values as host bindings', () => {
@ -3107,8 +3107,8 @@ describe('ngtsc behavioral tests', () => {
describe('listLazyRoutes()', () => {
// clang-format off
const lazyRouteMatching = (
route: string, fromModulePath: RegExp, fromModuleName: string, toModulePath: RegExp,
toModuleName: string) => {
route: string, fromModulePath: RegExp, fromModuleName: string, toModulePath: RegExp,
toModuleName: string) => {
return {
route,
module: jasmine.objectContaining({

View File

@ -51,8 +51,6 @@ export class Identifiers {
static elementStyling: o.ExternalReference = {name: 'ɵelementStyling', moduleName: CORE};
static elementHostAttrs: o.ExternalReference = {name: 'ɵelementHostAttrs', moduleName: CORE};
static elementStylingMap: o.ExternalReference = {name: 'ɵelementStylingMap', moduleName: CORE};
static elementStyleProp: o.ExternalReference = {name: 'ɵelementStyleProp', moduleName: CORE};
@ -60,6 +58,22 @@ export class Identifiers {
static elementStylingApply:
o.ExternalReference = {name: 'ɵelementStylingApply', moduleName: CORE};
static elementHostAttrs: o.ExternalReference = {name: 'ɵelementHostAttrs', moduleName: CORE};
static elementHostStyling: o.ExternalReference = {name: 'ɵelementHostStyling', moduleName: CORE};
static elementHostStylingMap:
o.ExternalReference = {name: 'ɵelementHostStylingMap', moduleName: CORE};
static elementHostStyleProp:
o.ExternalReference = {name: 'ɵelementHostStyleProp', moduleName: CORE};
static elementHostClassProp:
o.ExternalReference = {name: 'ɵelementHostClassProp', moduleName: CORE};
static elementHostStylingApply:
o.ExternalReference = {name: 'ɵelementHostStylingApply', moduleName: CORE};
static containerCreate: o.ExternalReference = {name: 'ɵcontainer', moduleName: CORE};
static nextContext: o.ExternalReference = {name: 'ɵnextContext', moduleName: CORE};

View File

@ -256,12 +256,12 @@ export class StylingBuilder {
reference: R3.elementHostAttrs,
allocateBindingSlots: 0,
buildParams: () => {
// params => elementHostAttrs(directive, attrs)
// params => elementHostAttrs(agetDirectiveContext()ttrs)
this.populateInitialStylingAttrs(attrs);
const attrArray = !attrs.some(attr => attr instanceof o.WrappedNodeExpr) ?
getConstantLiteralFromArray(constantPool, attrs) :
o.literalArr(attrs);
return [this._directiveExpr !, attrArray];
return [attrArray];
}
};
}
@ -276,11 +276,11 @@ export class StylingBuilder {
*/
buildElementStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
Instruction|null {
const reference = this._directiveExpr ? R3.elementHostStyling : R3.elementStyling;
if (this.hasBindings) {
return {
sourceSpan,
allocateBindingSlots: 0,
reference: R3.elementStyling,
allocateBindingSlots: 0, reference,
buildParams: () => {
// a string array of every style-based binding
const styleBindingProps =
@ -295,12 +295,17 @@ export class StylingBuilder {
// (otherwise a shorter amount of params will be filled). The code below helps
// determine how many params are required in the expression code.
//
// min params => elementStyling()
// max params => elementStyling(classBindings, styleBindings, sanitizer, directive)
// HOST:
// min params => elementHostStyling()
// max params => elementHostStyling(classBindings, styleBindings, sanitizer)
//
// Template:
// min params => elementStyling()
// max params => elementStyling(classBindings, styleBindings, sanitizer)
//
const params: o.Expression[] = [];
let expectedNumberOfArgs = 0;
if (this._directiveExpr) {
expectedNumberOfArgs = 4;
} else if (this._useDefaultSanitizer) {
if (this._useDefaultSanitizer) {
expectedNumberOfArgs = 3;
} else if (styleBindingProps.length) {
expectedNumberOfArgs = 2;
@ -308,7 +313,6 @@ export class StylingBuilder {
expectedNumberOfArgs = 1;
}
const params: o.Expression[] = [];
addParam(
params, classBindingNames.length > 0,
getConstantLiteralFromArray(constantPool, classBindingNames), 1,
@ -320,9 +324,6 @@ export class StylingBuilder {
addParam(
params, this._useDefaultSanitizer, o.importExpr(R3.defaultStyleSanitizer), 3,
expectedNumberOfArgs);
if (this._directiveExpr) {
params.push(this._directiveExpr);
}
return params;
}
};
@ -357,31 +358,40 @@ export class StylingBuilder {
totalBindingSlotsRequired += mapBasedStyleValue.expressions.length;
}
const isHostBinding = this._directiveExpr;
const reference = isHostBinding ? R3.elementHostStylingMap : R3.elementStylingMap;
return {
sourceSpan: stylingInput.sourceSpan,
reference: R3.elementStylingMap,
reference,
allocateBindingSlots: totalBindingSlotsRequired,
buildParams: (convertFn: (value: any) => o.Expression) => {
// min params => elementStylingMap(index, classMap)
// max params => elementStylingMap(index, classMap, styleMap, directive)
let expectedNumberOfArgs = 0;
if (this._directiveExpr) {
expectedNumberOfArgs = 4;
} else if (mapBasedStyleValue) {
expectedNumberOfArgs = 3;
} else if (mapBasedClassValue) {
// index and class = 2
expectedNumberOfArgs = 2;
// HOST:
// min params => elementHostStylingMap(classMap)
// max params => elementHostStylingMap(classMap, styleMap)
// Template:
// min params => elementStylingMap(elmIndex, classMap)
// max params => elementStylingMap(elmIndex, classMap, styleMap)
const params: o.Expression[] = [];
if (!isHostBinding) {
params.push(this._elementIndexExpr);
}
let expectedNumberOfArgs = 0;
if (mapBasedStyleValue) {
expectedNumberOfArgs = 2;
} else if (mapBasedClassValue) {
// index and class = 2
expectedNumberOfArgs = 1;
}
const params: o.Expression[] = [this._elementIndexExpr];
addParam(
params, mapBasedClassValue, mapBasedClassValue ? convertFn(mapBasedClassValue) : null,
2, expectedNumberOfArgs);
1, expectedNumberOfArgs);
addParam(
params, mapBasedStyleValue, mapBasedStyleValue ? convertFn(mapBasedStyleValue) : null,
3, expectedNumberOfArgs);
addParam(params, this._directiveExpr, this._directiveExpr, 4, expectedNumberOfArgs);
2, expectedNumberOfArgs);
return params;
}
};
@ -390,8 +400,9 @@ export class StylingBuilder {
}
private _buildSingleInputs(
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
allowUnits: boolean, valueConverter: ValueConverter): Instruction[] {
reference: o.ExternalReference, isHostBinding: boolean, inputs: BoundStylingEntry[],
mapIndex: Map<string, number>, allowUnits: boolean,
valueConverter: ValueConverter): Instruction[] {
let totalBindingSlotsRequired = 0;
return inputs.map(input => {
const bindingIndex: number = mapIndex.get(input.name !) !;
@ -401,25 +412,29 @@ export class StylingBuilder {
sourceSpan: input.sourceSpan,
allocateBindingSlots: totalBindingSlotsRequired, reference,
buildParams: (convertFn: (value: any) => o.Expression) => {
// min params => elementStlyingProp(elmIndex, bindingIndex, value)
// max params => elementStlyingProp(elmIndex, bindingIndex, value, overrideFlag)
// HOST:
// min params => elementHostStylingProp(bindingIndex, value)
// max params => elementHostStylingProp(bindingIndex, value, overrideFlag)
// Template:
// min params => elementStylingProp(elmIndex, bindingIndex, value)
// max params => elementStylingProp(elmIndex, bindingIndex, value, overrideFlag)
const params: o.Expression[] = [];
const params = [this._elementIndexExpr, o.literal(bindingIndex), convertFn(value)];
if (!isHostBinding) {
params.push(this._elementIndexExpr);
}
params.push(o.literal(bindingIndex));
params.push(convertFn(value));
if (allowUnits) {
if (input.unit) {
params.push(o.literal(input.unit));
} else if (this._directiveExpr) {
} else if (input.hasOverrideFlag) {
params.push(o.NULL_EXPR);
}
}
if (this._directiveExpr) {
params.push(this._directiveExpr);
} else if (input.hasOverrideFlag) {
params.push(o.NULL_EXPR);
}
if (input.hasOverrideFlag) {
params.push(o.literal(true));
}
@ -432,33 +447,39 @@ export class StylingBuilder {
private _buildClassInputs(valueConverter: ValueConverter): Instruction[] {
if (this._singleClassInputs) {
const isHostBinding = !!this._directiveExpr;
const reference = isHostBinding ? R3.elementHostClassProp : R3.elementClassProp;
return this._buildSingleInputs(
R3.elementClassProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
reference, isHostBinding, this._singleClassInputs, this._classesIndex, false,
valueConverter);
}
return [];
}
private _buildStyleInputs(valueConverter: ValueConverter): Instruction[] {
if (this._singleStyleInputs) {
const isHostBinding = !!this._directiveExpr;
const reference = isHostBinding ? R3.elementHostStyleProp : R3.elementStyleProp;
return this._buildSingleInputs(
R3.elementStyleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
reference, isHostBinding, this._singleStyleInputs, this._stylesIndex, true,
valueConverter);
}
return [];
}
private _buildApplyFn(): Instruction {
const isHostBinding = this._directiveExpr;
const reference = isHostBinding ? R3.elementHostStylingApply : R3.elementStylingApply;
return {
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
reference: R3.elementStylingApply,
reference,
allocateBindingSlots: 0,
buildParams: () => {
// min params => elementStylingApply(elmIndex)
// max params => elementStylingApply(elmIndex, directive)
const params: o.Expression[] = [this._elementIndexExpr];
if (this._directiveExpr) {
params.push(this._directiveExpr);
}
return params;
// HOST:
// params => elementHostStylingApply()
// Template:
// params => elementStylingApply(elmIndex)
return isHostBinding ? [] : [this._elementIndexExpr];
}
};
}

View File

@ -99,11 +99,18 @@ export {
elementContainerStart as ɵelementContainerStart,
elementContainerEnd as ɵelementContainerEnd,
elementStyling as ɵelementStyling,
elementHostAttrs as ɵelementHostAttrs,
elementStylingMap as ɵelementStylingMap,
elementStyleProp as ɵelementStyleProp,
elementStylingApply as ɵelementStylingApply,
elementClassProp as ɵelementClassProp,
elementHostAttrs as ɵelementHostAttrs,
elementHostStyling as ɵelementHostStyling,
elementHostStylingMap as ɵelementHostStylingMap,
elementHostStyleProp as ɵelementHostStyleProp,
elementHostClassProp as ɵelementHostClassProp,
elementHostStylingApply as ɵelementHostStylingApply,
flushHooksUpTo as ɵflushHooksUpTo,
textBinding as ɵtextBinding,
template as ɵtemplate,

View File

@ -52,11 +52,17 @@ export {
elementContainerStart,
elementContainerEnd,
elementStyling,
elementHostAttrs,
elementStylingMap,
elementStyleProp,
elementStylingApply,
elementHostAttrs,
elementHostStyling,
elementHostStylingMap,
elementHostStyleProp,
elementHostClassProp,
elementHostStylingApply,
flushHooksUpTo,
listener,

View File

@ -5,4 +5,5 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './instructions';
export * from './instructions';
export * from './styling_instructions';

View File

@ -13,11 +13,9 @@ import {Type} from '../../interface/type';
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema';
import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization';
import {Sanitizer} from '../../sanitization/security';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual} from '../../util/assert';
import {isObservable} from '../../util/lang';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
import {assertHasParent, assertLContainerOrUndefined, assertLView, assertPreviousIsParent} from '../assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
import {attachPatchData, getComponentViewByInstance} from '../context_discovery';
@ -29,7 +27,6 @@ import {ACTIVE_INDEX, LContainer, VIEWS} from '../interfaces/container';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
import {PlayerFactory} from '../interfaces/player';
import {CssSelectorList} from '../interfaces/projection';
import {LQueries} from '../interfaces/query';
import {GlobalTargetResolver, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
@ -40,16 +37,17 @@ import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
import {appendChild, appendProjectedNodes, createTextNode, insertView, removeView} from '../node_manipulation';
import {isNodeMatchingSelectorList, matchingProjectionSelectorIndex} from '../node_selector_matcher';
import {applyOnCreateInstructions} from '../node_util';
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode} from '../state';
import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from '../styling/class_and_style_bindings';
import {BoundPlayerFactory} from '../styling/player_factory';
import {ANIMATION_PROP_PREFIX, allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput, isAnimationProp} from '../styling/util';
import {decreaseElementDepthCount, enterView, getActiveHostContext, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setActiveHost, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode} from '../state';
import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles} from '../styling/class_and_style_bindings';
import {ANIMATION_PROP_PREFIX, getStylingContext, hasClassInput, hasStyleInput, isAnimationProp} from '../styling/util';
import {NO_CHANGE} from '../tokens';
import {attrsStylingIndexOf, setUpAttributes} from '../util/attrs_utils';
import {INTERPOLATION_DELIMITER, renderStringify} from '../util/misc_utils';
import {findComponentView, getLViewParent, getRootContext, getRootView} from '../util/view_traversal_utils';
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
import {setInputsForProperty} from './shared';
/**
@ -140,9 +138,10 @@ export function setHostBindings(tView: TView, viewData: LView): void {
// If it's not a number, it's a host binding function that needs to be executed.
if (instruction !== null) {
viewData[BINDING_INDEX] = bindingRootIndex;
instruction(
RenderFlags.Update, unwrapRNode(viewData[currentDirectiveIndex]),
currentElementIndex);
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
setActiveHost(hostCtx, currentElementIndex);
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
setActiveHost(null);
}
currentDirectiveIndex++;
}
@ -1328,32 +1327,6 @@ export function createTNode(
};
}
/**
* Set the inputs of directives at the current node to corresponding value.
*
* @param lView the `LView` which contains the directives.
* @param inputAliases mapping between the public "input" name and privately-known,
* possibly minified, property names to write to.
* @param value Value to set.
*/
function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: any): void {
const tView = lView[TVIEW];
for (let i = 0; i < inputs.length;) {
const index = inputs[i++] as number;
const publicName = inputs[i++] as string;
const privateName = inputs[i++] as string;
const instance = lView[index];
ngDevMode && assertDataInRange(lView, index);
const def = tView.data[index] as DirectiveDef<any>;
const setInput = def.setInput;
if (setInput) {
def.setInput !(instance, value, publicName, privateName);
} else {
instance[privateName] = value;
}
}
}
function setNgReflectProperties(
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
value: any) {
@ -1411,63 +1384,6 @@ function generatePropertyAliases(tNode: TNode, direction: BindingDirection): Pro
return propStore;
}
/**
* Assign any inline style values to the element during creation mode.
*
* This instruction is meant to be called during creation mode to register all
* dynamic style and class bindings on the element. Note for static values (no binding)
* see `elementStart` and `elementHostAttrs`.
*
* @param classBindingNames An array containing bindable class names.
* The `elementClassProp` refers to the class name by index in this array.
* (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
* @param styleBindingNames An array containing bindable style properties.
* The `elementStyleProp` refers to the class name by index in this array.
* (i.e. `['width', 'height']` means `width=0` and `height=1`).
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
* property values that are applied to the element (during rendering).
* Note that the sanitizer instance itself is tied to the `directive` (if provided).
* @param directive A directive instance the styling is associated with. If not provided
* current view's controller instance is assumed.
*
* @publicApi
*/
export function elementStyling(
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
styleSanitizer?: StyleSanitizeFn | null, directive?: {}): void {
const tNode = getPreviousOrParentTNode();
if (!tNode.stylingTemplate) {
tNode.stylingTemplate = createEmptyStylingContext();
}
if (directive) {
// this will ALWAYS happen first before the bindings are applied so that the ordering
// of directives is correct (otherwise if a follow-up directive contains static styling,
// which is applied through elementHostAttrs, then it may end up being listed in the
// context directive array before a former one (because the former one didn't contain
// any static styling values))
allocateDirectiveIntoContext(tNode.stylingTemplate, directive);
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
fns.push(
() => initElementStyling(
tNode, classBindingNames, styleBindingNames, styleSanitizer, directive));
} else {
// this will make sure that the root directive (the template) will always be
// run FIRST before all the other styling properties are populated into the
// context...
initElementStyling(tNode, classBindingNames, styleBindingNames, styleSanitizer, directive);
}
}
function initElementStyling(
tNode: TNode, classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
styleSanitizer?: StyleSanitizeFn | null, directive?: {}): void {
updateContextWithBindings(
tNode.stylingTemplate !, directive || null, classBindingNames, styleBindingNames,
styleSanitizer);
}
/**
* Assign static attribute values to a host element.
*
@ -1507,13 +1423,14 @@ function initElementStyling(
*
* @publicApi
*/
export function elementHostAttrs(directive: any, attrs: TAttributes) {
export function elementHostAttrs(attrs: TAttributes) {
const tNode = getPreviousOrParentTNode();
const lView = getLView();
const native = getNativeByTNode(tNode, lView) as RElement;
const lastAttrIndex = setUpAttributes(native, attrs);
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex);
if (stylingAttrsStartIndex >= 0) {
const directive = getActiveHostContext();
if (tNode.stylingTemplate) {
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, stylingAttrsStartIndex, directive);
} else {
@ -1523,171 +1440,6 @@ export function elementHostAttrs(directive: any, attrs: TAttributes) {
}
}
/**
* Apply styling binding to the element.
*
* This instruction is meant to be run after `elementStyle` and/or `elementStyleProp`.
* if any styling bindings have changed then the changes are flushed to the element.
*
*
* @param index Index of the element's with which styling is associated.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
components
*
* @publicApi
*/
export function elementStylingApply(index: number, directive?: any): void {
const lView = getLView();
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
const totalPlayersQueued = renderStyling(
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender, null,
null, directive);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(lView);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
}
}
/**
* Update a style bindings value on an element.
*
* If the style value is `null` then it will be removed from the element
* (or assigned a different value depending if there are any styles placed
* on the element with `elementStyle` or any styles that are present
* from when the element was created (with `elementStyling`).
*
* (Note that the styling element is updated as part of `elementStylingApply`.)
*
* @param index Index of the element's with which styling is associated.
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `elementStlyingBindings`.
* @param value New value to write (null to remove). Note that if a directive also
* attempts to write to the same binding value then it will only be able to
* do so if the template binding value is `null` (or doesn't exist at all).
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
components
*
* @publicApi
*/
export function elementStyleProp(
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
suffix?: string | null, directive?: {}, forceOverride?: boolean): void {
let valueToAdd: string|null = null;
if (value !== null) {
if (suffix) {
// when a suffix is applied then it will bypass
// sanitization entirely (b/c a new string is created)
valueToAdd = renderStringify(value) + suffix;
} else {
// sanitization happens by dealing with a String value
// this means that the string value will be passed through
// into the style rendering later (which is where the value
// will be sanitized before it is applied)
valueToAdd = value as any as string;
}
}
updateElementStyleProp(
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd, directive,
forceOverride);
}
/**
* Add or remove a class via a class binding on a DOM element.
*
* This instruction is meant to handle the [class.foo]="exp" case and, therefore,
* the class itself must already be applied using `elementStyling` within
* the creation block.
*
* @param index Index of the element's with which styling is associated.
* @param classIndex Index of class to toggle. This index value refers to the
* index of the class in the class bindings array that was passed into
* `elementStlyingBindings` (which is meant to be called before this
* function is).
* @param value A true/false value which will turn the class on or off.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
* @param forceOverride Whether or not this value will be applied regardless of where it is being
* set within the directive priority structure.
*
* @publicApi
*/
export function elementClassProp(
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {},
forceOverride?: boolean): void {
const input = (value instanceof BoundPlayerFactory) ?
(value as BoundPlayerFactory<boolean|null>) :
booleanOrNull(value);
updateElementClassProp(
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, input, directive,
forceOverride);
}
function booleanOrNull(value: any): boolean|null {
if (typeof value === 'boolean') return value;
return value ? true : null;
}
/**
* Update style and/or class bindings using object literal.
*
* This instruction is meant apply styling via the `[style]="exp"` and `[class]="exp"` template
* bindings. When styles are applied to the element they will then be placed with respect to
* any styles set with `elementStyleProp`. If any styles are set to `null` then they will be
* removed from the element. This instruction is also called for host bindings that write to
* `[style]` and `[class]` (the directive param helps the instruction code determine where the
* binding values come from).
*
* (Note that the styling instruction will not be applied until `elementStylingApply` is called.)
*
* @param index Index of the element's with which styling is associated.
* @param classes A key/value style map of CSS classes that will be added to the given element.
* Any missing classes (that have already been applied to the element beforehand) will be
* removed (unset) from the element's list of CSS classes.
* @param styles A key/value style map of the styles that will be applied to the given element.
* Any missing styles (that have already been applied to the element beforehand) will be
* removed (unset) from the element's styling.
* @param directive Directive instance that is attempting to change styling. (Defaults to the
* component of the current view).
*
* @publicApi
*/
export function elementStylingMap<T>(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null, directive?: {}): void {
const lView = getLView();
const tNode = getTNode(index, lView);
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
// inputs are only evaluated from a template binding into a directive, therefore,
// there should not be a situation where a directive host bindings function
// evaluates the inputs (this should only happen in the template function)
if (!directive) {
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
const initialClasses = getInitialClassNameValue(stylingContext);
const classInputVal =
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
classes = NO_CHANGE;
}
if (hasStyleInput(tNode) && styles !== NO_CHANGE) {
const initialStyles = getInitialClassNameValue(stylingContext);
const styleInputVal =
(initialStyles.length ? (initialStyles + ' ') : '') + forceStylesAsString(styles);
setInputsForProperty(lView, tNode.inputs !['style'] !, styleInputVal);
styles = NO_CHANGE;
}
}
updateStylingMap(stylingContext, classes, styles, directive);
}
//////////////////////////
//// Text
//////////////////////////
@ -1838,7 +1590,10 @@ export function invokeHostBindingsInCreationMode(
firstTemplatePass: boolean) {
const previousExpandoLength = expando.length;
setCurrentDirectiveDef(def);
def.hostBindings !(RenderFlags.Create, directive, tNode.index - HEADER_OFFSET);
const elementIndex = tNode.index - HEADER_OFFSET;
setActiveHost(directive, elementIndex);
def.hostBindings !(RenderFlags.Create, directive, elementIndex);
setActiveHost(null);
setCurrentDirectiveDef(null);
// `hostBindings` function may or may not contain `allocHostVars` call
// (e.g. it may not if it only contains host listeners), so we need to check whether
@ -2712,6 +2467,24 @@ export function markViewDirty(lView: LView): LView|null {
return null;
}
/**
* Used to perform change detection on the whole application.
*
* This is equivalent to `detectChanges`, but invoked on root component. Additionally, `tick`
* executes lifecycle hooks and conditionally checks components based on their
* `ChangeDetectionStrategy` and dirtiness.
*
* The preferred way to trigger change detection is to call `markDirty`. `markDirty` internally
* schedules `tick` using a scheduler in order to coalesce multiple `markDirty` calls into a
* single change detection run. By default, the scheduler is `requestAnimationFrame`, but can
* be changed when calling `renderComponent` and providing the `scheduler` option.
*/
export function tick<T>(component: T): void {
const rootView = getRootView(component);
const rootContext = rootView[CONTEXT] as RootContext;
tickRootContext(rootContext);
}
/**
* Used to schedule change detection on the whole application.
*
@ -2750,24 +2523,6 @@ export function scheduleTick<T>(rootContext: RootContext, flags: RootContextFlag
}
}
/**
* Used to perform change detection on the whole application.
*
* This is equivalent to `detectChanges`, but invoked on root component. Additionally, `tick`
* executes lifecycle hooks and conditionally checks components based on their
* `ChangeDetectionStrategy` and dirtiness.
*
* The preferred way to trigger change detection is to call `markDirty`. `markDirty` internally
* schedules `tick` using a scheduler in order to coalesce multiple `markDirty` calls into a
* single change detection run. By default, the scheduler is `requestAnimationFrame`, but can
* be changed when calling `renderComponent` and providing the `scheduler` option.
*/
export function tick<T>(component: T): void {
const rootView = getRootView(component);
const rootContext = rootView[CONTEXT] as RootContext;
tickRootContext(rootContext);
}
function tickRootContext(rootContext: RootContext) {
for (let i = 0; i < rootContext.components.length; i++) {
const rootComponent = rootContext.components[i];
@ -3332,4 +3087,4 @@ function handleError(lView: LView, error: any): void {
const injector = lView[INJECTOR];
const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
errorHandler && errorHandler.handleError(error);
}
}

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {assertDataInRange} from '../../util/assert';
import {DirectiveDef} from '../interfaces/definition';
import {PropertyAliasValue} from '../interfaces/node';
import {LView, TVIEW} from '../interfaces/view';
/**
* Set the inputs of directives at the current node to corresponding value.
*
* @param lView the `LView` which contains the directives.
* @param inputAliases mapping between the public "input" name and privately-known,
* possibly minified, property names to write to.
* @param value Value to set.
*/
export function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: any): void {
const tView = lView[TVIEW];
for (let i = 0; i < inputs.length;) {
const index = inputs[i++] as number;
const publicName = inputs[i++] as string;
const privateName = inputs[i++] as string;
const instance = lView[index];
ngDevMode && assertDataInRange(lView, index);
const def = tView.data[index] as DirectiveDef<any>;
const setInput = def.setInput;
if (setInput) {
def.setInput !(instance, value, publicName, privateName);
} else {
instance[privateName] = value;
}
}
}

View File

@ -0,0 +1,421 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {TNode} from '../interfaces/node';
import {PlayerFactory} from '../interfaces/player';
import {FLAGS, HEADER_OFFSET, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view';
import {getActiveHostContext, getActiveHostElementIndex, getLView, getPreviousOrParentTNode} from '../state';
import {getInitialClassNameValue, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from '../styling/class_and_style_bindings';
import {BoundPlayerFactory} from '../styling/player_factory';
import {allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput} from '../styling/util';
import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {getRootContext} from '../util/view_traversal_utils';
import {getTNode} from '../util/view_utils';
import {scheduleTick} from './instructions';
import {setInputsForProperty} from './shared';
/*
* The contents of this file include the instructions for all styling-related
* operations in Angular.
*
* The instructions present in this file are:
*
* Template level styling instructions:
* - elementStyling
* - elementStylingMap
* - elementStyleProp
* - elementClassProp
* - elementStylingApply
*
* Host bindings level styling instructions:
* - elementHostStyling
* - elementHostStylingMap
* - elementHostStyleProp
* - elementHostClassProp
* - elementHostStylingApply
*/
/**
* Allocates style and class binding properties on the element during creation mode.
*
* This instruction is meant to be called during creation mode to register all
* dynamic style and class bindings on the element. Note that this is only used
* for binding values (see `elementStart` to learn how to assign static styling
* values to an element).
*
* @param classBindingNames An array containing bindable class names.
* The `elementClassProp` instruction refers to the class name by index in
* this array (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
* @param styleBindingNames An array containing bindable style properties.
* The `elementStyleProp` instruction refers to the class name by index in
* this array (i.e. `['width', 'height']` means `width=0` and `height=1`).
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
* style values that are applied to the element (during rendering).
*
* @publicApi
*/
export function elementStyling(
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
styleSanitizer?: StyleSanitizeFn | null): void {
const tNode = getPreviousOrParentTNode();
if (!tNode.stylingTemplate) {
tNode.stylingTemplate = createEmptyStylingContext();
}
// calling the function below ensures that the template's binding values
// are applied as the first set of bindings into the context. If any other
// styling bindings are set on the same element (by directives and/or
// components) then they will be applied at the end of the `elementEnd`
// instruction (because directives are created first before styling is
// executed for a new element).
initElementStyling(tNode, classBindingNames, styleBindingNames, styleSanitizer, null);
}
/**
* Allocates style and class binding properties on the host element during creation mode
* within the host bindings function of a directive or component.
*
* This instruction is meant to be called during creation mode to register all
* dynamic style and class host bindings on the host element of a directive or
* component. Note that this is only used for binding values (see `elementHostAttrs`
* to learn how to assign static styling values to the host element).
*
* @param classBindingNames An array containing bindable class names.
* The `elementHostClassProp` instruction refers to the class name by index in
* this array (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
* @param styleBindingNames An array containing bindable style properties.
* The `elementHostStyleProp` instruction refers to the class name by index in
* this array (i.e. `['width', 'height']` means `width=0` and `height=1`).
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
* style values that are applied to the element (during rendering).
* Note that the sanitizer instance itself is tied to the provided `directive` and
* will not be used if the same property is assigned in another directive or
* on the element directly.
*
* @publicApi
*/
export function elementHostStyling(
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
styleSanitizer?: StyleSanitizeFn | null): void {
const tNode = getPreviousOrParentTNode();
if (!tNode.stylingTemplate) {
tNode.stylingTemplate = createEmptyStylingContext();
}
const directive = getActiveHostContext();
// despite the binding being applied in a queue (below), the allocation
// of the directive into the context happens right away. The reason for
// this is to retain the ordering of the directives (which is important
// for the prioritization of bindings).
allocateDirectiveIntoContext(tNode.stylingTemplate, directive);
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
fns.push(
() => initElementStyling(
tNode, classBindingNames, styleBindingNames, styleSanitizer, directive));
}
function initElementStyling(
tNode: TNode, classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
styleSanitizer?: StyleSanitizeFn | null, directive?: {} | null): void {
updateContextWithBindings(
tNode.stylingTemplate !, directive || null, classBindingNames, styleBindingNames,
styleSanitizer);
}
/**
* Update a style binding on an element with the provided value.
*
* If the style value is falsy then it will be removed from the element
* (or assigned a different value depending if there are any styles placed
* on the element with `elementStylingMap` or any static styles that are
* present from when the element was created with `elementStyling`).
*
* Note that the styling element is updated as part of `elementStylingApply`.
*
* @param index Index of the element's with which styling is associated.
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `elementStyling`.
* @param value New value to write (falsy to remove). Note that if a directive also
* attempts to write to the same binding value (via `elementHostStyleProp`)
* then it will only be able to do so if the binding value assigned via
* `elementStyleProp` is falsy (or doesn't exist at all).
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
* @param forceOverride Whether or not to update the styling value immediately
* (despite the other bindings possibly having priority)
*
* @publicApi
*/
export function elementStyleProp(
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
suffix?: string | null, forceOverride?: boolean): void {
elementStylePropInternal(null, index, styleIndex, value, suffix, forceOverride);
}
/**
* Update a host style binding value on the host element within a component/directive.
*
* If the style value is falsy then it will be removed from the host element
* (or assigned a different value depending if there are any styles placed
* on the same element with `elementHostStylingMap` or any static styles that
* are present from when the element was patched with `elementHostStyling`).
*
* Note that the styling applied to the host element once
* `elementHostStylingApply` is called.
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `elementHostStyling`.
* @param value New value to write (falsy to remove). The value may or may not
* be applied to the element depending on the template/component/directive
* prioritization (see `interfaces/styling.ts`)
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
* @param forceOverride Whether or not to update the styling value immediately
* (despite the other bindings possibly having priority)
*
* @publicApi
*/
export function elementHostStyleProp(
styleIndex: number, value: string | number | String | PlayerFactory | null,
suffix?: string | null, forceOverride?: boolean): void {
elementStylePropInternal(
getActiveHostContext() !, getActiveHostElementIndex() !, styleIndex, value, suffix,
forceOverride);
}
function elementStylePropInternal(
directive: {} | null, index: number, styleIndex: number,
value: string | number | String | PlayerFactory | null, suffix?: string | null,
forceOverride?: boolean): void {
let valueToAdd: string|null = null;
if (value !== null) {
if (suffix) {
// when a suffix is applied then it will bypass
// sanitization entirely (b/c a new string is created)
valueToAdd = renderStringify(value) + suffix;
} else {
// sanitization happens by dealing with a String value
// this means that the string value will be passed through
// into the style rendering later (which is where the value
// will be sanitized before it is applied)
valueToAdd = value as any as string;
}
}
updateElementStyleProp(
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd, directive,
forceOverride);
}
/**
* Update a class binding on an element with the provided value.
*
* This instruction is meant to handle the `[class.foo]="exp"` case and,
* therefore, the class binding itself must already be allocated using
* `elementStyling` within the creation block.
*
* @param index Index of the element's with which styling is associated.
* @param classIndex Index of class to toggle. This index value refers to the
* index of the class in the class bindings array that was passed into
* `elementStyling` (which is meant to be called before this
* function is).
* @param value A true/false value which will turn the class on or off.
* @param forceOverride Whether or not this value will be applied regardless
* of where it is being set within the styling priority structure.
*
* @publicApi
*/
export function elementClassProp(
index: number, classIndex: number, value: boolean | PlayerFactory,
forceOverride?: boolean): void {
elementClassPropInternal(null, index, classIndex, value, forceOverride);
}
/**
* Update a class host binding for a directive's/component's host element within
* the host bindings function.
*
* This instruction is meant to handle the `@HostBinding('class.foo')` case and,
* therefore, the class binding itself must already be allocated using
* `elementHostStyling` within the creation block.
*
* @param classIndex Index of class to toggle. This index value refers to the
* index of the class in the class bindings array that was passed into
* `elementHostStlying` (which is meant to be called before this
* function is).
* @param value A true/false value which will turn the class on or off.
* @param forceOverride Whether or not this value will be applied regardless
* of where it is being set within the stylings priority structure.
*
* @publicApi
*/
export function elementHostClassProp(
classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void {
elementClassPropInternal(
getActiveHostContext() !, getActiveHostElementIndex() !, classIndex, value, forceOverride);
}
function elementClassPropInternal(
directive: {} | null, index: number, classIndex: number, value: boolean | PlayerFactory,
forceOverride?: boolean): void {
const input = (value instanceof BoundPlayerFactory) ?
(value as BoundPlayerFactory<boolean|null>) :
booleanOrNull(value);
updateElementClassProp(
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, input, directive,
forceOverride);
}
function booleanOrNull(value: any): boolean|null {
if (typeof value === 'boolean') return value;
return value ? true : null;
}
/**
* Update style and/or class bindings using object literals on an element.
*
* This instruction is meant to apply styling via the `[style]="exp"` and `[class]="exp"` template
* bindings. When styles/classes are applied to the element they will then be updated with
* respect to any styles/classes set with `elementStyleProp` or `elementClassProp`. If any
* styles or classes are set to falsy then they will be removed from the element.
*
* Note that the styling instruction will not be applied until `elementStylingApply` is called.
*
* @param index Index of the element's with which styling is associated.
* @param classes A key/value map or string of CSS classes that will be added to the
* given element. Any missing classes (that have already been applied to the element
* beforehand) will be removed (unset) from the element's list of CSS classes.
* @param styles A key/value style map of the styles that will be applied to the given element.
* Any missing styles (that have already been applied to the element beforehand) will be
* removed (unset) from the element's styling.
*
* @publicApi
*/
export function elementStylingMap(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
elementStylingMapInternal(null, index, classes, styles);
}
/**
* Update style and/or class host bindings using object literals on an element within the host
* bindings function for a directive/component.
*
* This instruction is meant to apply styling via the `@HostBinding('style')` and
* `@HostBinding('class')` bindings for a component's or directive's host element.
* When styles/classes are applied to the host element they will then be updated
* with respect to any styles/classes set with `elementHostStyleProp` or
* `elementHostClassProp`. If any styles or classes are set to falsy then they
* will be removed from the element.
*
* Note that the styling instruction will not be applied until
* `elementHostStylingApply` is called.
*
* @param classes A key/value map or string of CSS classes that will be added to the
* given element. Any missing classes (that have already been applied to the element
* beforehand) will be removed (unset) from the element's list of CSS classes.
* @param styles A key/value style map of the styles that will be applied to the given element.
* Any missing styles (that have already been applied to the element beforehand) will be
* removed (unset) from the element's styling.
*
* @publicApi
*/
export function elementHostStylingMap(
classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
elementStylingMapInternal(
getActiveHostContext() !, getActiveHostElementIndex() !, classes, styles);
}
function elementStylingMapInternal(
directive: {} | null, index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
const lView = getLView();
const tNode = getTNode(index, lView);
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
// inputs are only evaluated from a template binding into a directive, therefore,
// there should not be a situation where a directive host bindings function
// evaluates the inputs (this should only happen in the template function)
if (!directive) {
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
const initialClasses = getInitialClassNameValue(stylingContext);
const classInputVal =
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
classes = NO_CHANGE;
}
if (hasStyleInput(tNode) && styles !== NO_CHANGE) {
const initialStyles = getInitialClassNameValue(stylingContext);
const styleInputVal =
(initialStyles.length ? (initialStyles + ' ') : '') + forceStylesAsString(styles);
setInputsForProperty(lView, tNode.inputs !['style'] !, styleInputVal);
styles = NO_CHANGE;
}
}
updateStylingMap(stylingContext, classes, styles, directive);
}
/**
* Apply all style and class binding values to the element.
*
* This instruction is meant to be run after `elementStylingMap`, `elementStyleProp`
* or `elementClassProp` instructions have been run and will only apply styling to
* the element if any styling bindings have been updated.
*
* @param index Index of the element's with which styling is associated.
*
* @publicApi
*/
export function elementStylingApply(index: number): void {
elementStylingApplyInternal(null, index);
}
/**
* Apply all style and class host binding values to the element.
*
* This instruction is meant to be run after `elementHostStylingMap`,
* `elementHostStyleProp` or `elementHostClassProp` instructions have
* been run and will only apply styling to the host element if any
* styling bindings have been updated.
*
* @publicApi
*/
export function elementHostStylingApply(): void {
elementStylingApplyInternal(getActiveHostContext() !, getActiveHostElementIndex() !);
}
export function elementStylingApplyInternal(directive: {} | null, index: number): void {
const lView = getLView();
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
const totalPlayersQueued = renderStyling(
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender, null,
null, directive);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(lView);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
}
}

View File

@ -72,7 +72,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵinterpolation7': r3.interpolation7,
'ɵinterpolation8': r3.interpolation8,
'ɵinterpolationV': r3.interpolationV,
'ɵelementClassProp': r3.elementClassProp,
'ɵlistener': r3.listener,
'ɵload': r3.load,
'ɵprojection': r3.projection,
@ -94,11 +93,17 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵcontentQuery': r3.contentQuery,
'ɵloadContentQuery': r3.loadContentQuery,
'ɵreference': r3.reference,
'ɵelementStyling': r3.elementStyling,
'ɵelementHostAttrs': r3.elementHostAttrs,
'ɵelementStyling': r3.elementStyling,
'ɵelementStylingMap': r3.elementStylingMap,
'ɵelementStyleProp': r3.elementStyleProp,
'ɵelementStylingApply': r3.elementStylingApply,
'ɵelementClassProp': r3.elementClassProp,
'ɵelementHostStyling': r3.elementHostStyling,
'ɵelementHostStylingMap': r3.elementHostStylingMap,
'ɵelementHostStyleProp': r3.elementHostStyleProp,
'ɵelementHostStylingApply': r3.elementHostStylingApply,
'ɵelementHostClassProp': r3.elementHostClassProp,
'ɵflushHooksUpTo': r3.flushHooksUpTo,
'ɵtemplate': r3.template,
'ɵtext': r3.text,

View File

@ -119,6 +119,29 @@ export function getLView(): LView {
return lView;
}
let activeHostContext: {}|null = null;
let activeHostElementIndex: number|null = null;
/**
* Sets the active host context (the directive/component instance) and its host element index.
*
* @param host the directive/component instance
* @param index the element index value for the host element where the directive/component instance
* lives
*/
export function setActiveHost(host: {} | null, index: number | null = null) {
activeHostContext = host;
activeHostElementIndex = index;
}
export function getActiveHostContext() {
return activeHostContext;
}
export function getActiveHostElementIndex() {
return activeHostElementIndex;
}
/**
* Restores `contextViewData` to the given OpaqueViewState instance.
*

View File

@ -623,6 +623,9 @@
{
"name": "saveResolvedLocalsInData"
},
{
"name": "setActiveHost"
},
{
"name": "setBindingRoot"
},

View File

@ -434,6 +434,9 @@
{
"name": "resetPreOrderHookFlags"
},
{
"name": "setActiveHost"
},
{
"name": "setBindingRoot"
},

View File

@ -572,6 +572,9 @@
{
"name": "elementClassProp"
},
{
"name": "elementClassPropInternal"
},
{
"name": "elementCreate"
},
@ -593,6 +596,9 @@
{
"name": "elementStylingApply"
},
{
"name": "elementStylingApplyInternal"
},
{
"name": "enterView"
},
@ -1214,6 +1220,9 @@
{
"name": "searchTokensOnInjector"
},
{
"name": "setActiveHost"
},
{
"name": "setBindingRoot"
},

View File

@ -9,7 +9,7 @@
import {ElementRef, QueryList, ViewContainerRef} from '@angular/core';
import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index';
import {allocHostVars, bind, directiveInject, element, elementAttribute, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, elementHostAttrs} from '../../src/render3/instructions/all';
import {allocHostVars, bind, directiveInject, element, elementAttribute, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, elementHostAttrs, elementHostStylingApply, elementHostStyleProp, elementHostStyling} from '../../src/render3/instructions/all';
import {loadContentQuery, contentQuery, queryRefresh} from '../../src/render3/query';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
@ -968,7 +968,7 @@ describe('host bindings', () => {
factory: () => new HostAttributeDir(),
hostBindings: function(rf, ctx, elIndex) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, ['role', 'listbox']);
elementHostAttrs(['role', 'listbox']);
}
}
});
@ -1107,11 +1107,11 @@ describe('host bindings', () => {
vars: 0,
hostBindings: (rf: RenderFlags, ctx: HostBindingToStyles, elIndex: number) => {
if (rf & RenderFlags.Create) {
elementStyling(null, ['width'], null, ctx);
elementHostStyling(null, ['width']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(0, 0, ctx.width, 'px', ctx);
elementStylingApply(0, ctx);
elementHostStyleProp(0, ctx.width, 'px');
elementHostStylingApply();
}
},
template: (rf: RenderFlags, cmp: HostBindingToStyles) => {}
@ -1151,11 +1151,11 @@ describe('host bindings', () => {
factory: () => hostBindingDir = new HostBindingToStyles(),
hostBindings: (rf: RenderFlags, ctx: HostBindingToStyles, elIndex: number) => {
if (rf & RenderFlags.Create) {
elementStyling(null, ['width'], null, ctx);
elementHostStyling(null, ['width']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(0, 0, ctx.width, 'px', ctx);
elementStylingApply(0, ctx);
elementHostStyleProp(0, ctx.width, 'px');
elementHostStylingApply();
}
}
});
@ -1202,11 +1202,11 @@ describe('host bindings', () => {
vars: 0,
hostBindings: (rf: RenderFlags, ctx: StaticHostClass, elIndex: number) => {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, [AttributeMarker.Classes, 'mat-toolbar']);
elementStyling(['mat-toolbar'], null, null, ctx);
elementHostAttrs([AttributeMarker.Classes, 'mat-toolbar']);
elementHostStyling(['mat-toolbar']);
}
if (rf & RenderFlags.Update) {
elementStylingApply(0, ctx);
elementHostStylingApply();
}
},
template: (rf: RenderFlags, cmp: StaticHostClass) => {}

View File

@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RendererType2} from '../../src/render/api';
import {AttributeMarker, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, elementStart, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, element, elementStyling, elementStylingApply, elementStyleProp, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, projection, projectionDef, reference, text, textBinding, template, elementStylingMap, directiveInject, elementHostAttrs} from '../../src/render3/instructions/all';
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, elementStart, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, element, elementStyling, elementStylingApply, elementStyleProp, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, projection, projectionDef, reference, text, textBinding, template, elementStylingMap, directiveInject, elementHostAttrs, elementHostStyleProp, elementHostStyling, elementHostClassProp, elementHostStylingApply, elementHostStylingMap} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, CONTEXT} from '../../src/render3/interfaces/view';
@ -480,7 +480,7 @@ describe('render3 integration test', () => {
vars: 0,
hostBindings: function(rf, ctx, elIndex) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, ['role', 'button']);
elementHostAttrs(['role', 'button']);
}
},
template: (rf: RenderFlags, ctx: HostAttributeComp) => {},
@ -1764,7 +1764,7 @@ describe('render3 integration test', () => {
hostBindings: function(
rf: RenderFlags, ctx: DirWithInitialStyling, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, [
elementHostAttrs([
'title', 'foo', AttributeMarker.Classes, 'heavy', 'golden',
AttributeMarker.Styles, 'color', 'purple', 'font-weight', 'bold'
]);
@ -1816,15 +1816,14 @@ describe('render3 integration test', () => {
rf: RenderFlags, ctx: DirWithSingleStylingBindings, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementHostAttrs(
ctx,
[AttributeMarker.Classes, 'def', AttributeMarker.Styles, 'width', '555px']);
elementStyling(['xyz'], ['width', 'height'], null, ctx);
elementHostStyling(['xyz'], ['width', 'height']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(elementIndex, 0, ctx.width, null, ctx);
elementStyleProp(elementIndex, 1, ctx.height, null, ctx);
elementClassProp(elementIndex, 0, ctx.activateXYZClass, ctx);
elementStylingApply(elementIndex, ctx);
elementHostStyleProp(0, ctx.width);
elementHostStyleProp(1, ctx.height);
elementHostClassProp(0, ctx.activateXYZClass);
elementHostStylingApply();
}
}
});
@ -1891,11 +1890,11 @@ describe('render3 integration test', () => {
factory: () => dir1Instance = new Dir1WithStyle(),
hostBindings: function(rf: RenderFlags, ctx: Dir1WithStyle, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementStyling(null, ['width'], null, ctx);
elementHostStyling(null, ['width']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(elementIndex, 0, ctx.width, null, ctx);
elementStylingApply(elementIndex, ctx);
elementHostStyleProp(0, ctx.width);
elementHostStylingApply();
}
}
});
@ -1915,12 +1914,12 @@ describe('render3 integration test', () => {
factory: () => dir2Instance = new Dir2WithStyle(),
hostBindings: function(rf: RenderFlags, ctx: Dir2WithStyle, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, [AttributeMarker.Styles, 'width', '111px']);
elementStyling(null, ['width'], null, ctx);
elementHostAttrs([AttributeMarker.Styles, 'width', '111px']);
elementHostStyling(null, ['width']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(elementIndex, 0, ctx.width, null, ctx);
elementStylingApply(elementIndex, ctx);
elementHostStyleProp(0, ctx.width);
elementHostStylingApply();
}
}
});
@ -1988,11 +1987,11 @@ describe('render3 integration test', () => {
factory: () => dir1Instance = new Dir1WithStyling(),
hostBindings: function(rf: RenderFlags, ctx: Dir1WithStyling, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementStyling(null, null, null, ctx);
elementHostStyling();
}
if (rf & RenderFlags.Update) {
elementStylingMap(elementIndex, ctx.classesExp, ctx.stylesExp, ctx);
elementStylingApply(elementIndex, ctx);
elementHostStylingMap(ctx.classesExp, ctx.stylesExp);
elementHostStylingApply();
}
}
});
@ -2014,12 +2013,12 @@ describe('render3 integration test', () => {
factory: () => dir2Instance = new Dir2WithStyling(),
hostBindings: function(rf: RenderFlags, ctx: Dir2WithStyling, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, [AttributeMarker.Styles, 'width', '111px']);
elementStyling(null, null, null, ctx);
elementHostAttrs([AttributeMarker.Styles, 'width', '111px']);
elementHostStyling();
}
if (rf & RenderFlags.Update) {
elementStylingMap(elementIndex, null, ctx.stylesExp, ctx);
elementStylingApply(elementIndex, ctx);
elementHostStylingMap(null, ctx.stylesExp);
elementHostStylingApply();
}
}
});

View File

@ -8,7 +8,7 @@
import {createRootContext} from '../../../src/render3/component';
import {getLContext} from '../../../src/render3/context_discovery';
import {defineComponent, defineDirective} from '../../../src/render3/index';
import {createLView, createTView, elementClassProp, elementEnd, elementHostAttrs, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, namespaceSVG} from '../../../src/render3/instructions/all';
import {createLView, createTView, elementClassProp, elementEnd, elementHostAttrs, elementHostClassProp, elementHostStyleProp, elementHostStyling, elementHostStylingApply, elementHostStylingMap, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, namespaceSVG} from '../../../src/render3/instructions/all';
import {RenderFlags} from '../../../src/render3/interfaces/definition';
import {AttributeMarker, TAttributes} from '../../../src/render3/interfaces/node';
import {BindingStore, BindingType, PlayState, Player, PlayerContext, PlayerFactory, PlayerHandler} from '../../../src/render3/interfaces/player';
@ -3238,12 +3238,12 @@ describe('style and class based bindings', () => {
factory: () => new MyDir(),
hostBindings: function(rf: RenderFlags, ctx: MyDir, elementIndex: number) {
if (rf & RenderFlags.Create) {
elementStyling(['foo'], ['width'], null, ctx);
elementHostStyling(['foo'], ['width']);
}
if (rf & RenderFlags.Update) {
elementStyleProp(0, 0, ctx.widthFactory, null, ctx);
elementClassProp(0, 0, ctx.fooFactory, ctx);
elementStylingApply(0, ctx);
elementHostStyleProp(0, ctx.widthFactory);
elementHostClassProp(0, ctx.fooFactory);
elementHostStylingApply();
}
}
});

View File

@ -1862,7 +1862,7 @@ describe('ViewContainerRef', () => {
template: (rf: RenderFlags, cmp: HostBindingCmpt) => {},
hostBindings: function(rf: RenderFlags, ctx: HostBindingCmpt, elIndex: number) {
if (rf & RenderFlags.Create) {
elementHostAttrs(ctx, ['id', 'attribute']);
elementHostAttrs(['id', 'attribute']);
allocHostVars(1);
}
if (rf & RenderFlags.Update) {