fix(ivy): i18n should not alloc expando slots when there is no new var (#31451)

`i18nStart` was calling `allocExpando` even if there was 0 new variable created.
This created a new expando instruction with the value 0 which was later interpreted as the start of a new expando block instead of just skipping 0 instructions.

FW-1417 #resolve

PR Close #31451
This commit is contained in:
Olivier Combe 2019-07-08 14:59:10 +02:00 committed by Jason Aden
parent 989ebcbb62
commit 2b44be984e
3 changed files with 60 additions and 17 deletions

View File

@ -471,7 +471,9 @@ function i18nStartFirstPass(
}
}
allocExpando(viewData, i18nVarsCount);
if (i18nVarsCount > 0) {
allocExpando(viewData, i18nVarsCount);
}
ngDevMode &&
attachI18nOpCodesDebug(

View File

@ -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);
}
}
}
}

View File

@ -1463,6 +1463,30 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
.toEqual(`<div-query>Contenu<!--ng-container--></div-query>`);
});
});
it('should not alloc expando slots when there is no new variable to create', () => {
@Component({
template: `
<div dialog i18n>
<div *ngIf="data">
Some content
</div>
</div>
<button [close]="true">Button label</button>
`
})
class ContentElementDialog {
data = false;
}
TestBed.configureTestingModule({declarations: [DialogDir, CloseBtn, ContentElementDialog]});
const fixture = TestBed.createComponent(ContentElementDialog);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual(`<div dialog=""><!--bindings={
"ng-reflect-ng-if": "false"
}--></div><button ng-reflect-dialog-result="true" title="Close dialog">Button label</button>`);
});
});
function initWithTemplate(compType: Type<any>, 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';
}