diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index a24d51b4c3..ae19790c0b 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -471,7 +471,9 @@ function i18nStartFirstPass( } } - allocExpando(viewData, i18nVarsCount); + if (i18nVarsCount > 0) { + allocExpando(viewData, i18nVarsCount); + } ngDevMode && attachI18nOpCodesDebug( diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 3d215c4ad2..c25a81cae5 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -11,7 +11,7 @@ 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 {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual, assertNotSame} from '../../util/assert'; +import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertLessThan, assertNotEqual, assertNotSame} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {assertLView, assertPreviousIsParent} from '../assert'; @@ -316,24 +316,31 @@ export function assignTViewNodeToLView( * When elements are created dynamically after a view blueprint is created (e.g. through * i18nApply() or ComponentFactory.create), we need to adjust the blueprint for future * template passes. + * + * @param view The LView containing the blueprint to adjust + * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0 */ export function allocExpando(view: LView, numSlotsToAlloc: number) { - const tView = view[TVIEW]; - if (tView.firstTemplatePass) { - for (let i = 0; i < numSlotsToAlloc; i++) { - tView.blueprint.push(null); - tView.data.push(null); - view.push(null); - } + ngDevMode && assertGreaterThan( + numSlotsToAlloc, 0, 'The number of slots to alloc should be greater than 0'); + if (numSlotsToAlloc > 0) { + const tView = view[TVIEW]; + if (tView.firstTemplatePass) { + for (let i = 0; i < numSlotsToAlloc; i++) { + tView.blueprint.push(null); + tView.data.push(null); + view.push(null); + } - // We should only increment the expando start index if there aren't already directives - // and injectors saved in the "expando" section - if (!tView.expandoInstructions) { - tView.expandoStartIndex += numSlotsToAlloc; - } else { - // Since we're adding the dynamic nodes into the expando section, we need to let the host - // bindings know that they should skip x slots - tView.expandoInstructions.push(numSlotsToAlloc); + // We should only increment the expando start index if there aren't already directives + // and injectors saved in the "expando" section + if (!tView.expandoInstructions) { + tView.expandoStartIndex += numSlotsToAlloc; + } else { + // Since we're adding the dynamic nodes into the expando section, we need to let the host + // bindings know that they should skip x slots + tView.expandoInstructions.push(numSlotsToAlloc); + } } } } diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index 2ba68ebafd..b4677823b4 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -1463,6 +1463,30 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { .toEqual(`Contenu`); }); }); + + it('should not alloc expando slots when there is no new variable to create', () => { + @Component({ + template: ` +
+
+ Some content +
+
+ + ` + }) + class ContentElementDialog { + data = false; + } + + TestBed.configureTestingModule({declarations: [DialogDir, CloseBtn, ContentElementDialog]}); + + const fixture = TestBed.createComponent(ContentElementDialog); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
`); + }); }); function initWithTemplate(compType: Type, template: string) { @@ -1491,3 +1515,13 @@ class DirectiveWithTplRef { class UppercasePipe implements PipeTransform { transform(value: string) { return value.toUpperCase(); } } + +@Directive({selector: `[dialog]`}) +export class DialogDir { +} + +@Directive({selector: `button[close]`, host: {'[title]': 'name'}}) +export class CloseBtn { + @Input('close') dialogResult: any; + name: string = 'Close dialog'; +}