refactor(ivy): remove def.attributes in favor of the `elementHostAttrs` instruction (#28089)
Up until this point, all static attribute values (things like `title` and `id`) defined within the `host` are of a Component/Directive definition were generated into a `def.attributes` array and then processed at runtime. This design decision does not lend itself well to tree-shaking and is inconsistent with other static values such as styles and classes. This fix ensures that all static attribute values (attributes, classes, and styles) that exist within a host definition for components and directives are all assigned via the `elementHostAttrs` instruction. ``` // before defineDirective({ ... attributes: ['title', 'my title'] ... }) //now defineDirective({ ... hostBindings: function() { if (create) { elementHostAttrs(..., ['title', 'my-title']); } ... } ... }) ``` PR Close #28089
This commit is contained in:
parent
e62eeed7d4
commit
693045165c
|
@ -296,11 +296,17 @@ describe('compiler compliance: bindings', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const HostAttributeDirDeclaration = `
|
const HostAttributeDirDeclaration = `
|
||||||
|
const $c0$ = ["aria-label", "label"];
|
||||||
|
…
|
||||||
HostAttributeDir.ngDirectiveDef = $r3$.ɵdefineDirective({
|
HostAttributeDir.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||||
type: HostAttributeDir,
|
type: HostAttributeDir,
|
||||||
selectors: [["", "hostAttributeDir", ""]],
|
selectors: [["", "hostAttributeDir", ""]],
|
||||||
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
||||||
attributes: ["aria-label", "label"]
|
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵelementHostAttrs(ctx, $c0$);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -310,6 +316,75 @@ describe('compiler compliance: bindings', () => {
|
||||||
expectEmit(source, HostAttributeDirDeclaration, 'Invalid host attribute code');
|
expectEmit(source, HostAttributeDirDeclaration, 'Invalid host attribute code');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support host attributes together with host classes and styles', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, Directive, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-host-attribute-component',
|
||||||
|
template: "...",
|
||||||
|
host: {
|
||||||
|
'title': 'hello there from component',
|
||||||
|
'style': 'opacity:1'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class HostAttributeComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[hostAttributeDir]',
|
||||||
|
host: {
|
||||||
|
'style': 'width: 200px; height: 500px',
|
||||||
|
'[style.opacity]': "true",
|
||||||
|
'class': 'one two',
|
||||||
|
'[class.three]': "true",
|
||||||
|
'title': 'hello there from directive',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class HostAttributeDir {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [HostAttributeComp, HostAttributeDir]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const CompAndDirDeclaration = `
|
||||||
|
const $c0$ = ["title", "hello there from component", ${AttributeMarker.Styles}, "opacity", "1"];
|
||||||
|
const $c1$ = ["title", "hello there from directive", ${AttributeMarker.Classes}, "one", "two", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||||
|
…
|
||||||
|
HostAttributeComp.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: HostAttributeComp,
|
||||||
|
selectors: [["my-host-attribute-component"]],
|
||||||
|
factory: function HostAttributeComp_Factory(t) { return new (t || HostAttributeComp)(); },
|
||||||
|
hostBindings: function HostAttributeComp_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵelementHostAttrs(ctx, $c0$);
|
||||||
|
…
|
||||||
|
}
|
||||||
|
…
|
||||||
|
}
|
||||||
|
…
|
||||||
|
HostAttributeDir.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||||
|
type: HostAttributeDir,
|
||||||
|
selectors: [["", "hostAttributeDir", ""]],
|
||||||
|
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
||||||
|
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵelementHostAttrs(ctx, $c1$);
|
||||||
|
…
|
||||||
|
}
|
||||||
|
…
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const source = result.source;
|
||||||
|
expectEmit(source, CompAndDirDeclaration, 'Invalid host attribute code');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('non bindable behavior', () => {
|
describe('non bindable behavior', () => {
|
||||||
|
|
|
@ -1100,7 +1100,7 @@ describe('compiler compliance: styling', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
const $_c0$ = [${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
const $_c0$ = ["title", "foo title", ${AttributeMarker.Classes}, "foo", "baz", ${AttributeMarker.Styles}, "width", "200px", "height", "500px"];
|
||||||
…
|
…
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
|
@ -1114,9 +1114,7 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
||||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
consts: 0,
|
|
||||||
vars: 0,
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
const result = compile(files, angularFiles);
|
||||||
|
|
|
@ -29,7 +29,7 @@ import {Render3ParseResult} from '../r3_template_transform';
|
||||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
||||||
|
|
||||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
||||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
import {Instruction, StylingBuilder} from './styling_builder';
|
||||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
|
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
|
||||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
|
||||||
|
|
||||||
|
@ -101,14 +101,11 @@ function baseDirectiveFields(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// e.g. `attributes: ['role', 'listbox']`
|
|
||||||
definitionMap.set('attributes', createHostAttributesArray(allOtherAttributes));
|
|
||||||
|
|
||||||
// e.g. `hostBindings: (rf, ctx, elIndex) => { ... }
|
// e.g. `hostBindings: (rf, ctx, elIndex) => { ... }
|
||||||
definitionMap.set(
|
definitionMap.set(
|
||||||
'hostBindings',
|
'hostBindings', createHostBindingsFunction(
|
||||||
createHostBindingsFunction(
|
meta, elVarExp, contextVarExp, allOtherAttributes, styleBuilder,
|
||||||
meta, elVarExp, contextVarExp, styleBuilder, bindingParser, constantPool, hostVarsCount));
|
bindingParser, constantPool, hostVarsCount));
|
||||||
|
|
||||||
// e.g 'inputs: {a: 'a'}`
|
// e.g 'inputs: {a: 'a'}`
|
||||||
definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
|
definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
|
||||||
|
@ -504,16 +501,13 @@ function createDirectiveSelector(selector: string | null): o.Expression {
|
||||||
return asLiteral(core.parseSelectorToR3Selector(selector));
|
return asLiteral(core.parseSelectorToR3Selector(selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHostAttributesArray(attributes: any): o.Expression|null {
|
function convertAttributesToExpressions(attributes: any): o.Expression[] {
|
||||||
const values: o.Expression[] = [];
|
const values: o.Expression[] = [];
|
||||||
for (let key of Object.getOwnPropertyNames(attributes)) {
|
for (let key of Object.getOwnPropertyNames(attributes)) {
|
||||||
const value = attributes[key];
|
const value = attributes[key];
|
||||||
values.push(o.literal(key), o.literal(value));
|
values.push(o.literal(key), o.literal(value));
|
||||||
}
|
}
|
||||||
if (values.length > 0) {
|
return values;
|
||||||
return o.literalArr(values);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a contentQueries function or null if one is not necessary.
|
// Return a contentQueries function or null if one is not necessary.
|
||||||
|
@ -649,8 +643,8 @@ function createViewQueriesFunction(
|
||||||
// Return a host binding function or null if one is not necessary.
|
// Return a host binding function or null if one is not necessary.
|
||||||
function createHostBindingsFunction(
|
function createHostBindingsFunction(
|
||||||
meta: R3DirectiveMetadata, elVarExp: o.ReadVarExpr, bindingContext: o.ReadVarExpr,
|
meta: R3DirectiveMetadata, elVarExp: o.ReadVarExpr, bindingContext: o.ReadVarExpr,
|
||||||
styleBuilder: StylingBuilder, bindingParser: BindingParser, constantPool: ConstantPool,
|
staticAttributesAndValues: any[], styleBuilder: StylingBuilder, bindingParser: BindingParser,
|
||||||
hostVarsCount: number): o.Expression|null {
|
constantPool: ConstantPool, hostVarsCount: number): o.Expression|null {
|
||||||
const createStatements: o.Statement[] = [];
|
const createStatements: o.Statement[] = [];
|
||||||
const updateStatements: o.Statement[] = [];
|
const updateStatements: o.Statement[] = [];
|
||||||
|
|
||||||
|
@ -740,18 +734,20 @@ function createHostBindingsFunction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styleBuilder.hasBindingsOrInitialValues()) {
|
// since we're dealing with directives/components and both have hostBinding
|
||||||
// since we're dealing with directives here and directives have a hostBinding
|
// functions, we need to generate a special hostAttrs instruction that deals
|
||||||
// function, we need to generate special instructions that deal with styling
|
// with both the assignment of styling as well as static attributes to the host
|
||||||
// (both bindings and initial values). The instruction below will instruct
|
// element. The instruction below will instruct all initial styling (styling
|
||||||
// all initial styling (styling that is inside of a host binding within a
|
// that is inside of a host binding within a directive/component) to be attached
|
||||||
// directive) to be attached to the host element of the directive.
|
// to the host element alongside any of the provided host attributes that were
|
||||||
const hostAttrsInstruction =
|
// collected earlier.
|
||||||
styleBuilder.buildDirectiveHostAttrsInstruction(null, constantPool);
|
const hostAttrs = convertAttributesToExpressions(staticAttributesAndValues);
|
||||||
if (hostAttrsInstruction) {
|
const hostInstruction = styleBuilder.buildHostAttrsInstruction(null, hostAttrs, constantPool);
|
||||||
createStatements.push(createStylingStmt(hostAttrsInstruction, bindingContext, bindingFn));
|
if (hostInstruction) {
|
||||||
}
|
createStatements.push(createStylingStmt(hostInstruction, bindingContext, bindingFn));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (styleBuilder.hasBindingsOrInitialValues()) {
|
||||||
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
|
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
|
||||||
// MUST be registered on a given element within the component/directive
|
// MUST be registered on a given element within the component/directive
|
||||||
// templateFn/hostBindingsFn functions. The instruction below will figure out
|
// templateFn/hostBindingsFn functions. The instruction below will figure out
|
||||||
|
@ -799,7 +795,7 @@ function createHostBindingsFunction(
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStylingStmt(
|
function createStylingStmt(
|
||||||
instruction: StylingInstruction, bindingContext: any, bindingFn: Function): o.Statement {
|
instruction: Instruction, bindingContext: any, bindingFn: Function): o.Statement {
|
||||||
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);
|
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);
|
||||||
return o.importExpr(instruction.reference, null, instruction.sourceSpan)
|
return o.importExpr(instruction.reference, null, instruction.sourceSpan)
|
||||||
.callFn(params, instruction.sourceSpan)
|
.callFn(params, instruction.sourceSpan)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {ValueConverter} from './template';
|
||||||
/**
|
/**
|
||||||
* A styling expression summary that is to be processed by the compiler
|
* A styling expression summary that is to be processed by the compiler
|
||||||
*/
|
*/
|
||||||
export interface StylingInstruction {
|
export interface Instruction {
|
||||||
sourceSpan: ParseSourceSpan|null;
|
sourceSpan: ParseSourceSpan|null;
|
||||||
reference: o.ExternalReference;
|
reference: o.ExternalReference;
|
||||||
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
|
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
|
||||||
|
@ -225,17 +225,17 @@ export class StylingBuilder {
|
||||||
* Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
* Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
||||||
*
|
*
|
||||||
* The instruction generation code below is used for producing the AOT statement code which is
|
* The instruction generation code below is used for producing the AOT statement code which is
|
||||||
* responsible for registering initial styles (within a directive hostBindings' creation block)
|
* responsible for registering initial styles (within a directive hostBindings' creation block),
|
||||||
* to the directive host element.
|
* as well as any of the provided attribute values, to the directive host element.
|
||||||
*/
|
*/
|
||||||
buildDirectiveHostAttrsInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
buildHostAttrsInstruction(
|
||||||
StylingInstruction|null {
|
sourceSpan: ParseSourceSpan|null, attrs: o.Expression[],
|
||||||
if (this._hasInitialValues && this._directiveExpr) {
|
constantPool: ConstantPool): Instruction|null {
|
||||||
|
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
||||||
return {
|
return {
|
||||||
sourceSpan,
|
sourceSpan,
|
||||||
reference: R3.elementHostAttrs,
|
reference: R3.elementHostAttrs,
|
||||||
buildParams: () => {
|
buildParams: () => {
|
||||||
const attrs: o.Expression[] = [];
|
|
||||||
this.populateInitialStylingAttrs(attrs);
|
this.populateInitialStylingAttrs(attrs);
|
||||||
return [this._directiveExpr !, getConstantLiteralFromArray(constantPool, attrs)];
|
return [this._directiveExpr !, getConstantLiteralFromArray(constantPool, attrs)];
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ export class StylingBuilder {
|
||||||
* responsible for registering style/class bindings to an element.
|
* responsible for registering style/class bindings to an element.
|
||||||
*/
|
*/
|
||||||
buildElementStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
buildElementStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||||
StylingInstruction|null {
|
Instruction|null {
|
||||||
if (this._hasBindings) {
|
if (this._hasBindings) {
|
||||||
return {
|
return {
|
||||||
sourceSpan,
|
sourceSpan,
|
||||||
|
@ -312,7 +312,7 @@ export class StylingBuilder {
|
||||||
* which include the `[style]` and `[class]` expression params (if they exist) as well as
|
* which include the `[style]` and `[class]` expression params (if they exist) as well as
|
||||||
* the sanitizer and directive reference expression.
|
* the sanitizer and directive reference expression.
|
||||||
*/
|
*/
|
||||||
buildElementStylingMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
|
buildElementStylingMapInstruction(valueConverter: ValueConverter): Instruction|null {
|
||||||
if (this._classMapInput || this._styleMapInput) {
|
if (this._classMapInput || this._styleMapInput) {
|
||||||
const stylingInput = this._classMapInput ! || this._styleMapInput !;
|
const stylingInput = this._classMapInput ! || this._styleMapInput !;
|
||||||
|
|
||||||
|
@ -355,7 +355,7 @@ export class StylingBuilder {
|
||||||
|
|
||||||
private _buildSingleInputs(
|
private _buildSingleInputs(
|
||||||
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
|
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
|
||||||
allowUnits: boolean, valueConverter: ValueConverter): StylingInstruction[] {
|
allowUnits: boolean, valueConverter: ValueConverter): Instruction[] {
|
||||||
return inputs.map(input => {
|
return inputs.map(input => {
|
||||||
const bindingIndex: number = mapIndex.get(input.name) !;
|
const bindingIndex: number = mapIndex.get(input.name) !;
|
||||||
const value = input.value.visit(valueConverter);
|
const value = input.value.visit(valueConverter);
|
||||||
|
@ -381,7 +381,7 @@ export class StylingBuilder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildClassInputs(valueConverter: ValueConverter): Instruction[] {
|
||||||
if (this._singleClassInputs) {
|
if (this._singleClassInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.elementClassProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
|
R3.elementClassProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
|
||||||
|
@ -389,7 +389,7 @@ export class StylingBuilder {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildStyleInputs(valueConverter: ValueConverter): Instruction[] {
|
||||||
if (this._singleStyleInputs) {
|
if (this._singleStyleInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.elementStyleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
|
R3.elementStyleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
|
||||||
|
@ -397,7 +397,7 @@ export class StylingBuilder {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildApplyFn(): StylingInstruction {
|
private _buildApplyFn(): Instruction {
|
||||||
return {
|
return {
|
||||||
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
||||||
reference: R3.elementStylingApply,
|
reference: R3.elementStylingApply,
|
||||||
|
@ -416,7 +416,7 @@ export class StylingBuilder {
|
||||||
* into the update block of a template function or a directive hostBindings function.
|
* into the update block of a template function or a directive hostBindings function.
|
||||||
*/
|
*/
|
||||||
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
|
||||||
const instructions: StylingInstruction[] = [];
|
const instructions: Instruction[] = [];
|
||||||
if (this._hasBindings) {
|
if (this._hasBindings) {
|
||||||
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
|
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
|
||||||
if (mapInstruction) {
|
if (mapInstruction) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ import {I18nContext} from './i18n/context';
|
||||||
import {I18nMetaVisitor} from './i18n/meta';
|
import {I18nMetaVisitor} from './i18n/meta';
|
||||||
import {getSerializedI18nContent} from './i18n/serializer';
|
import {getSerializedI18nContent} from './i18n/serializer';
|
||||||
import {I18N_ICU_MAPPING_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
import {I18N_ICU_MAPPING_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
|
||||||
import {StylingBuilder, StylingInstruction} from './styling_builder';
|
import {Instruction, StylingBuilder} from './styling_builder';
|
||||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
||||||
|
|
||||||
// Default selector used by `<ng-content>` if none specified
|
// Default selector used by `<ng-content>` if none specified
|
||||||
|
@ -689,7 +689,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
if (input.type === BindingType.Animation) {
|
if (input.type === BindingType.Animation) {
|
||||||
const value = input.value.visit(this._valueConverter);
|
const value = input.value.visit(this._valueConverter);
|
||||||
// animation bindings can be presented in the following formats:
|
// animation bindings can be presented in the following formats:
|
||||||
// 1j [@binding]="fooExp"
|
// 1. [@binding]="fooExp"
|
||||||
// 2. [@binding]="{value:fooExp, params:{...}}"
|
// 2. [@binding]="{value:fooExp, params:{...}}"
|
||||||
// 3. [@binding]
|
// 3. [@binding]
|
||||||
// 4. @binding
|
// 4. @binding
|
||||||
|
@ -936,7 +936,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
private processStylingInstruction(
|
private processStylingInstruction(
|
||||||
implicit: any, instruction: StylingInstruction|null, createMode: boolean) {
|
implicit: any, instruction: Instruction|null, createMode: boolean) {
|
||||||
if (instruction) {
|
if (instruction) {
|
||||||
const paramsFn = () =>
|
const paramsFn = () =>
|
||||||
instruction.buildParams(value => this.convertPropertyBinding(implicit, value, true));
|
instruction.buildParams(value => this.convertPropertyBinding(implicit, value, true));
|
||||||
|
|
|
@ -68,14 +68,6 @@ export function defineComponent<T>(componentDefinition: {
|
||||||
*/
|
*/
|
||||||
vars: number;
|
vars: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* Static attributes to set on host element.
|
|
||||||
*
|
|
||||||
* Even indices: attribute name
|
|
||||||
* Odd indices: attribute value
|
|
||||||
*/
|
|
||||||
attributes?: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of input names.
|
* A map of input names.
|
||||||
*
|
*
|
||||||
|
@ -260,7 +252,6 @@ export function defineComponent<T>(componentDefinition: {
|
||||||
hostBindings: componentDefinition.hostBindings || null,
|
hostBindings: componentDefinition.hostBindings || null,
|
||||||
contentQueries: componentDefinition.contentQueries || null,
|
contentQueries: componentDefinition.contentQueries || null,
|
||||||
contentQueriesRefresh: componentDefinition.contentQueriesRefresh || null,
|
contentQueriesRefresh: componentDefinition.contentQueriesRefresh || null,
|
||||||
attributes: componentDefinition.attributes || null,
|
|
||||||
declaredInputs: declaredInputs,
|
declaredInputs: declaredInputs,
|
||||||
inputs: null !, // assigned in noSideEffects
|
inputs: null !, // assigned in noSideEffects
|
||||||
outputs: null !, // assigned in noSideEffects
|
outputs: null !, // assigned in noSideEffects
|
||||||
|
@ -516,14 +507,6 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
|
||||||
*/
|
*/
|
||||||
factory: (t: Type<T>| null) => T;
|
factory: (t: Type<T>| null) => T;
|
||||||
|
|
||||||
/**
|
|
||||||
* Static attributes to set on host element.
|
|
||||||
*
|
|
||||||
* Even indices: attribute name
|
|
||||||
* Odd indices: attribute value
|
|
||||||
*/
|
|
||||||
attributes?: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of input names.
|
* A map of input names.
|
||||||
*
|
*
|
||||||
|
|
|
@ -752,31 +752,62 @@ function createViewBlueprint(bindingStartIndex: number, initialViewLength: numbe
|
||||||
return blueprint;
|
return blueprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
/**
|
||||||
|
* Assigns all attribute values to the provided element via the inferred renderer.
|
||||||
|
*
|
||||||
|
* This function accepts two forms of attribute entries:
|
||||||
|
*
|
||||||
|
* default: (key, value):
|
||||||
|
* attrs = [key1, value1, key2, value2]
|
||||||
|
*
|
||||||
|
* namespaced: (NAMESPACE_MARKER, uri, name, value)
|
||||||
|
* attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
|
||||||
|
*
|
||||||
|
* The `attrs` array can contain a mix of both the default and namespaced entries.
|
||||||
|
* The "default" values are set without a marker, but if the function comes across
|
||||||
|
* a marker value then it will attempt to set a namespaced value. If the marker is
|
||||||
|
* not of a namespaced value then the function will quit and return the index value
|
||||||
|
* where it stopped during the iteration of the attrs array.
|
||||||
|
*
|
||||||
|
* See [AttributeMarker] to understand what the namespace marker value is.
|
||||||
|
*
|
||||||
|
* Note that this instruction does not support assigning style and class values to
|
||||||
|
* an element. See `elementStart` and `elementHostAttrs` to learn how styling values
|
||||||
|
* are applied to an element.
|
||||||
|
*
|
||||||
|
* @param native The element that the attributes will be assigned to
|
||||||
|
* @param attrs The attribute array of values that will be assigned to the element
|
||||||
|
* @returns the index value that was last accessed in the attributes array
|
||||||
|
*/
|
||||||
|
function setUpAttributes(native: RElement, attrs: TAttributes): number {
|
||||||
const renderer = getLView()[RENDERER];
|
const renderer = getLView()[RENDERER];
|
||||||
const isProc = isProceduralRenderer(renderer);
|
const isProc = isProceduralRenderer(renderer);
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
while (i < attrs.length) {
|
while (i < attrs.length) {
|
||||||
const attrName = attrs[i++];
|
const value = attrs[i];
|
||||||
if (typeof attrName == 'number') {
|
if (typeof value === 'number') {
|
||||||
if (attrName === AttributeMarker.NamespaceURI) {
|
// only namespaces are supported. Other value types (such as style/class
|
||||||
// Namespaced attributes
|
// entries) are not supported in this function.
|
||||||
const namespaceURI = attrs[i++] as string;
|
if (value !== AttributeMarker.NamespaceURI) {
|
||||||
const attrName = attrs[i++] as string;
|
|
||||||
const attrVal = attrs[i++] as string;
|
|
||||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
|
||||||
isProc ?
|
|
||||||
(renderer as ProceduralRenderer3)
|
|
||||||
.setAttribute(native, attrName, attrVal, namespaceURI) :
|
|
||||||
native.setAttributeNS(namespaceURI, attrName, attrVal);
|
|
||||||
} else {
|
|
||||||
// All other `AttributeMarker`s are ignored here.
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we just landed on the marker value ... therefore
|
||||||
|
// we should skip to the next entry
|
||||||
|
i++;
|
||||||
|
|
||||||
|
const namespaceURI = attrs[i++] as string;
|
||||||
|
const attrName = attrs[i++] as string;
|
||||||
|
const attrVal = attrs[i++] as string;
|
||||||
|
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||||
|
isProc ?
|
||||||
|
(renderer as ProceduralRenderer3).setAttribute(native, attrName, attrVal, namespaceURI) :
|
||||||
|
native.setAttributeNS(namespaceURI, attrName, attrVal);
|
||||||
} else {
|
} else {
|
||||||
/// attrName is string;
|
/// attrName is string;
|
||||||
const attrVal = attrs[i++];
|
const attrName = value as string;
|
||||||
|
const attrVal = attrs[++i];
|
||||||
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
||||||
// Standard attributes
|
// Standard attributes
|
||||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||||
|
@ -791,8 +822,15 @@ function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
||||||
native.setAttribute(attrName as string, attrVal as string);
|
native.setAttribute(attrName as string, attrVal as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// another piece of code may iterate over the same attributes array. Therefore
|
||||||
|
// it may be helpful to return the exact spot where the attributes array exited
|
||||||
|
// whether by running into an unsupported marker or if all the static values were
|
||||||
|
// iterated over.
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createError(text: string, token: any) {
|
export function createError(text: string, token: any) {
|
||||||
|
@ -1257,19 +1295,41 @@ export function elementStyling(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign static styling values to a host element.
|
* Assign static attribute values to a host element.
|
||||||
|
*
|
||||||
|
* This instruction will assign static attribute values as well as class and style
|
||||||
|
* values to an element within the host bindings function. Since attribute values
|
||||||
|
* can consist of different types of values, the `attrs` array must include the values in
|
||||||
|
* the following format:
|
||||||
|
*
|
||||||
|
* attrs = [
|
||||||
|
* // static attributes (like `title`, `name`, `id`...)
|
||||||
|
* attr1, value1, attr2, value,
|
||||||
|
*
|
||||||
|
* // a single namespace value (like `x:id`)
|
||||||
|
* NAMESPACE_MARKER, namespaceUri1, name1, value1,
|
||||||
|
*
|
||||||
|
* // another single namespace value (like `x:name`)
|
||||||
|
* NAMESPACE_MARKER, namespaceUri2, name2, value2,
|
||||||
|
*
|
||||||
|
* // a series of CSS classes that will be applied to the element (no spaces)
|
||||||
|
* CLASSES_MARKER, class1, class2, class3,
|
||||||
|
*
|
||||||
|
* // a series of CSS styles (property + value) that will be applied to the element
|
||||||
|
* STYLES_MARKER, prop1, value1, prop2, value2
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* All non-class and non-style attributes must be defined at the start of the list
|
||||||
|
* first before all class and style values are set. When there is a change in value
|
||||||
|
* type (like when classes and styles are introduced) a marker must be used to separate
|
||||||
|
* the entries. The marker values themselves are set via entries found in the
|
||||||
|
* [AttributeMarker] enum.
|
||||||
*
|
*
|
||||||
* NOTE: This instruction is meant to used from `hostBindings` function only.
|
* NOTE: This instruction is meant to used from `hostBindings` function only.
|
||||||
*
|
*
|
||||||
* @param directive A directive instance the styling is associated with.
|
* @param directive A directive instance the styling is associated with.
|
||||||
* @param attrs An array containing class and styling information. The values must be marked with
|
* @param attrs An array of static values (attributes, classes and styles) with the correct marker
|
||||||
* `AttributeMarker`.
|
* values.
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* var attrs = [AttributeMarker.Classes, 'foo', 'bar',
|
|
||||||
* AttributeMarker.Styles, 'width', '100px', 'height, '200px']
|
|
||||||
* elementHostAttrs(directive, attrs);
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
|
@ -1278,7 +1338,10 @@ export function elementHostAttrs(directive: any, attrs: TAttributes) {
|
||||||
if (!tNode.stylingTemplate) {
|
if (!tNode.stylingTemplate) {
|
||||||
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
|
tNode.stylingTemplate = initializeStaticStylingContext(attrs);
|
||||||
}
|
}
|
||||||
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, directive);
|
const lView = getLView();
|
||||||
|
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||||
|
const i = setUpAttributes(native, attrs);
|
||||||
|
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, i, directive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1658,11 +1721,6 @@ function postProcessBaseDirective<T>(
|
||||||
if (native) {
|
if (native) {
|
||||||
attachPatchData(native, lView);
|
attachPatchData(native, lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(misko): setUpAttributes should be a feature for better treeshakability.
|
|
||||||
if (def.attributes != null && previousOrParentTNode.type == TNodeType.Element) {
|
|
||||||
setUpAttributes(native as RElement, def.attributes as string[]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -140,14 +140,6 @@ export interface DirectiveDef<T> extends BaseDef<T> {
|
||||||
/** Refreshes host bindings on the associated directive. */
|
/** Refreshes host bindings on the associated directive. */
|
||||||
hostBindings: HostBindingsFunction<T>|null;
|
hostBindings: HostBindingsFunction<T>|null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Static attributes to set on host element.
|
|
||||||
*
|
|
||||||
* Even indices: attribute name
|
|
||||||
* Odd indices: attribute value
|
|
||||||
*/
|
|
||||||
readonly attributes: string[]|null;
|
|
||||||
|
|
||||||
/* The following are lifecycle hooks for this component */
|
/* The following are lifecycle hooks for this component */
|
||||||
onInit: (() => void)|null;
|
onInit: (() => void)|null;
|
||||||
doCheck: (() => void)|null;
|
doCheck: (() => void)|null;
|
||||||
|
|
|
@ -75,7 +75,7 @@ export function initializeStaticContext(attrs: TAttributes) {
|
||||||
* @param directive the directive instance with which static data is associated with.
|
* @param directive the directive instance with which static data is associated with.
|
||||||
*/
|
*/
|
||||||
export function patchContextWithStaticAttrs(
|
export function patchContextWithStaticAttrs(
|
||||||
context: StylingContext, attrs: TAttributes, directive: any): void {
|
context: StylingContext, attrs: TAttributes, startingIndex: number, directive: any): void {
|
||||||
// If the styling context has already been patched with the given directive's bindings,
|
// If the styling context has already been patched with the given directive's bindings,
|
||||||
// then there is no point in doing it again. The reason why this may happen (the directive
|
// then there is no point in doing it again. The reason why this may happen (the directive
|
||||||
// styling being patched twice) is because the `stylingBinding` function is called each time
|
// styling being patched twice) is because the `stylingBinding` function is called each time
|
||||||
|
@ -89,7 +89,7 @@ export function patchContextWithStaticAttrs(
|
||||||
let initialStyles: InitialStylingValues|null = null;
|
let initialStyles: InitialStylingValues|null = null;
|
||||||
|
|
||||||
let mode = -1;
|
let mode = -1;
|
||||||
for (let i = 0; i < attrs.length; i++) {
|
for (let i = startingIndex; i < attrs.length; i++) {
|
||||||
const attr = attrs[i];
|
const attr = attrs[i];
|
||||||
if (typeof attr == 'number') {
|
if (typeof attr == 'number') {
|
||||||
mode = attr;
|
mode = attr;
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{
|
{
|
||||||
"name": "ACTIVE_INDEX"
|
"name": "ACTIVE_INDEX"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ANIMATION_PROP_PREFIX"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "BINDING_INDEX"
|
"name": "BINDING_INDEX"
|
||||||
},
|
},
|
||||||
|
@ -77,9 +74,6 @@
|
||||||
{
|
{
|
||||||
"name": "NG_PIPE_DEF"
|
"name": "NG_PIPE_DEF"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "NG_PROJECT_AS_ATTR_NAME"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "NO_CHANGE"
|
"name": "NO_CHANGE"
|
||||||
},
|
},
|
||||||
|
@ -320,9 +314,6 @@
|
||||||
{
|
{
|
||||||
"name": "invertObject"
|
"name": "invertObject"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "isAnimationProp"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "isComponentDef"
|
"name": "isComponentDef"
|
||||||
},
|
},
|
||||||
|
@ -434,9 +425,6 @@
|
||||||
{
|
{
|
||||||
"name": "setTNodeAndViewData"
|
"name": "setTNodeAndViewData"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "setUpAttributes"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "syncViewWithBlueprint"
|
"name": "syncViewWithBlueprint"
|
||||||
},
|
},
|
||||||
|
|
|
@ -965,7 +965,11 @@ describe('host bindings', () => {
|
||||||
selectors: [['', 'hostAttributeDir', '']],
|
selectors: [['', 'hostAttributeDir', '']],
|
||||||
type: HostAttributeDir,
|
type: HostAttributeDir,
|
||||||
factory: () => new HostAttributeDir(),
|
factory: () => new HostAttributeDir(),
|
||||||
attributes: ['role', 'listbox']
|
hostBindings: function(rf, ctx, elIndex) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementHostAttrs(ctx, ['role', 'listbox']);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -478,8 +478,12 @@ describe('render3 integration test', () => {
|
||||||
factory: () => new HostAttributeComp(),
|
factory: () => new HostAttributeComp(),
|
||||||
consts: 0,
|
consts: 0,
|
||||||
vars: 0,
|
vars: 0,
|
||||||
|
hostBindings: function(rf, ctx, elIndex) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementHostAttrs(ctx, ['role', 'button']);
|
||||||
|
}
|
||||||
|
},
|
||||||
template: (rf: RenderFlags, ctx: HostAttributeComp) => {},
|
template: (rf: RenderFlags, ctx: HostAttributeComp) => {},
|
||||||
attributes: ['role', 'button']
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1706,8 +1710,8 @@ describe('render3 integration test', () => {
|
||||||
rf: RenderFlags, ctx: DirWithInitialStyling, elementIndex: number) {
|
rf: RenderFlags, ctx: DirWithInitialStyling, elementIndex: number) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementHostAttrs(ctx, [
|
elementHostAttrs(ctx, [
|
||||||
AttributeMarker.Classes, 'heavy', 'golden', AttributeMarker.Styles, 'color',
|
'title', 'foo', AttributeMarker.Classes, 'heavy', 'golden',
|
||||||
'purple', 'font-weight', 'bold'
|
AttributeMarker.Styles, 'color', 'purple', 'font-weight', 'bold'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1735,6 +1739,7 @@ describe('render3 integration test', () => {
|
||||||
const classes = target.getAttribute('class') !.split(/\s+/).sort();
|
const classes = target.getAttribute('class') !.split(/\s+/).sort();
|
||||||
expect(classes).toEqual(['big', 'golden', 'heavy']);
|
expect(classes).toEqual(['big', 'golden', 'heavy']);
|
||||||
|
|
||||||
|
expect(target.getAttribute('title')).toEqual('foo');
|
||||||
expect(target.style.getPropertyValue('color')).toEqual('black');
|
expect(target.style.getPropertyValue('color')).toEqual('black');
|
||||||
expect(target.style.getPropertyValue('font-size')).toEqual('200px');
|
expect(target.style.getPropertyValue('font-size')).toEqual('200px');
|
||||||
expect(target.style.getPropertyValue('font-weight')).toEqual('bold');
|
expect(target.style.getPropertyValue('font-weight')).toEqual('bold');
|
||||||
|
|
|
@ -70,7 +70,7 @@ describe('style and class based bindings', () => {
|
||||||
attrs.push(AttributeMarker.Styles);
|
attrs.push(AttributeMarker.Styles);
|
||||||
attrs.push(...styles);
|
attrs.push(...styles);
|
||||||
}
|
}
|
||||||
patchContextWithStaticAttrs(context, attrs, directiveRef || null);
|
patchContextWithStaticAttrs(context, attrs, 0, directiveRef || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRootContextInternal(lView: LView) { return lView[CONTEXT] as RootContext; }
|
function getRootContextInternal(lView: LView) { return lView[CONTEXT] as RootContext; }
|
||||||
|
@ -1388,7 +1388,7 @@ describe('style and class based bindings', () => {
|
||||||
let styles: any = {fontSize: ''};
|
let styles: any = {fontSize: ''};
|
||||||
updateStyleProp(stylingContext, 0, '');
|
updateStyleProp(stylingContext, 0, '');
|
||||||
updateStylingMap(stylingContext, null, styles);
|
updateStylingMap(stylingContext, null, styles);
|
||||||
patchContextWithStaticAttrs(stylingContext, [], otherDirective);
|
patchContextWithStaticAttrs(stylingContext, [], 0, otherDirective);
|
||||||
|
|
||||||
getStyles(stylingContext, otherDirective);
|
getStyles(stylingContext, otherDirective);
|
||||||
expect(store.getValues()).toEqual({});
|
expect(store.getValues()).toEqual({});
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, El
|
||||||
import {ViewEncapsulation} from '../../src/metadata';
|
import {ViewEncapsulation} from '../../src/metadata';
|
||||||
import {AttributeMarker, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load, query, queryRefresh} from '../../src/render3/index';
|
import {AttributeMarker, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load, query, queryRefresh} from '../../src/render3/index';
|
||||||
|
|
||||||
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
|
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding, elementHostAttrs} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {RElement} from '../../src/render3/interfaces/renderer';
|
import {RElement} from '../../src/render3/interfaces/renderer';
|
||||||
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
||||||
|
@ -1855,9 +1855,9 @@ describe('ViewContainerRef', () => {
|
||||||
consts: 0,
|
consts: 0,
|
||||||
vars: 0,
|
vars: 0,
|
||||||
template: (rf: RenderFlags, cmp: HostBindingCmpt) => {},
|
template: (rf: RenderFlags, cmp: HostBindingCmpt) => {},
|
||||||
attributes: ['id', 'attribute'],
|
|
||||||
hostBindings: function(rf: RenderFlags, ctx: HostBindingCmpt, elIndex: number) {
|
hostBindings: function(rf: RenderFlags, ctx: HostBindingCmpt, elIndex: number) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementHostAttrs(ctx, ['id', 'attribute']);
|
||||||
allocHostVars(1);
|
allocHostVars(1);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
|
Loading…
Reference in New Issue