From 8714daf276552938cf8b9fa7c632f3905f71be1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Fri, 15 Mar 2019 13:45:08 -0700 Subject: [PATCH] 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.
{}, 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(); } } }); diff --git a/packages/common/src/directives/ng_style.ts b/packages/common/src/directives/ng_style.ts index 4ae2dfcafc..ad34d52c5f 100644 --- a/packages/common/src/directives/ng_style.ts +++ b/packages/common/src/directives/ng_style.ts @@ -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(); } } }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts index 1f53bce95f..fd96a69438 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts @@ -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$); … } … diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts index e1eb6b02b5..0c0cf3c178 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts @@ -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(); } } `; diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 9535dbf276..980dbff559 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -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({ diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 29c13c0d67..f44cec5e7d 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -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}; diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts index 9dd5942071..ceee817f20 100644 --- a/packages/compiler/src/render3/view/styling_builder.ts +++ b/packages/compiler/src/render3/view/styling_builder.ts @@ -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, - allowUnits: boolean, valueConverter: ValueConverter): Instruction[] { + reference: o.ExternalReference, isHostBinding: boolean, inputs: BoundStylingEntry[], + mapIndex: Map, 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]; } }; } diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 2c69fe477a..04eb530977 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -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, diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 632ec3c4ec..da609eaabe 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -52,11 +52,17 @@ export { elementContainerStart, elementContainerEnd, elementStyling, - elementHostAttrs, elementStylingMap, elementStyleProp, elementStylingApply, + elementHostAttrs, + elementHostStyling, + elementHostStylingMap, + elementHostStyleProp, + elementHostClassProp, + elementHostStylingApply, + flushHooksUpTo, listener, diff --git a/packages/core/src/render3/instructions/all.ts b/packages/core/src/render3/instructions/all.ts index 5a9f5e7895..fd2c3e9712 100644 --- a/packages/core/src/render3/instructions/all.ts +++ b/packages/core/src/render3/instructions/all.ts @@ -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'; \ No newline at end of file +export * from './instructions'; +export * from './styling_instructions'; diff --git a/packages/core/src/render3/instructions/instructions.ts b/packages/core/src/render3/instructions/instructions.ts index b643dc7cbc..4f64b431ff 100644 --- a/packages/core/src/render3/instructions/instructions.ts +++ b/packages/core/src/render3/instructions/instructions.ts @@ -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; - 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) : - 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( - 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(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(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(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); -} +} \ No newline at end of file diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts new file mode 100644 index 0000000000..00121f3a65 --- /dev/null +++ b/packages/core/src/render3/instructions/shared.ts @@ -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; + const setInput = def.setInput; + if (setInput) { + def.setInput !(instance, value, publicName, privateName); + } else { + instance[privateName] = value; + } + } +} \ No newline at end of file diff --git a/packages/core/src/render3/instructions/styling_instructions.ts b/packages/core/src/render3/instructions/styling_instructions.ts new file mode 100644 index 0000000000..b06d0b2a4e --- /dev/null +++ b/packages/core/src/render3/instructions/styling_instructions.ts @@ -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) : + 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); + } +} \ No newline at end of file diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 780a45c1a6..cd4112cecd 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -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, diff --git a/packages/core/src/render3/state.ts b/packages/core/src/render3/state.ts index 54021c6b36..62095887c1 100644 --- a/packages/core/src/render3/state.ts +++ b/packages/core/src/render3/state.ts @@ -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. * diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 4bba4c6506..aca1ed856b 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -623,6 +623,9 @@ { "name": "saveResolvedLocalsInData" }, + { + "name": "setActiveHost" + }, { "name": "setBindingRoot" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index b660974683..12b3e484af 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -434,6 +434,9 @@ { "name": "resetPreOrderHookFlags" }, + { + "name": "setActiveHost" + }, { "name": "setBindingRoot" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 3923b09ef4..b0925d8341 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -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" }, diff --git a/packages/core/test/render3/host_binding_spec.ts b/packages/core/test/render3/host_binding_spec.ts index 556f93108a..bbb4168251 100644 --- a/packages/core/test/render3/host_binding_spec.ts +++ b/packages/core/test/render3/host_binding_spec.ts @@ -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) => {} diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index bbb4244ee0..008d18e935 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -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(); } } }); diff --git a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts index ba77af22ca..63a0a81125 100644 --- a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts +++ b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts @@ -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(); } } }); diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 95eb983140..6d5ec4a39b 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -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) {