Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ivy): correctly reflect undefined values #30103

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 12 additions & 7 deletions packages/core/src/render3/instructions/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -905,15 +905,20 @@ export function setNgReflectProperty(
attrName = normalizeDebugBindingName(attrName);
const debugValue = normalizeDebugBindingValue(value);
if (type === TNodeType.Element) {
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
} else if (value !== undefined) {
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (value == null) {
isProceduralRenderer(renderer) ? renderer.removeAttribute((element as RElement), attrName) :
(element as RElement).removeAttribute(attrName);
} else {
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
}
} else {
const textContent = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue((element as RComment), value);
renderer.setValue((element as RComment), textContent);
} else {
(element as RComment).textContent = value;
(element as RComment).textContent = textContent;
}
}
}
Expand Down
93 changes: 93 additions & 0 deletions packages/core/test/linker/integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1772,6 +1772,99 @@ function declareTests(config?: {useJit: boolean}) {
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('[ERROR]');
});

it('should not reflect undefined values', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);

fixture.componentInstance.ctxProp = 'hello';
fixture.detectChanges();

expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('ng-reflect-dir-prop="hello"');

fixture.componentInstance.ctxProp = undefined !;
fixture.detectChanges();

expect(getDOM().getInnerHTML(fixture.nativeElement)).not.toContain('ng-reflect-');
});

it('should not reflect null values', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);

fixture.componentInstance.ctxProp = 'hello';
fixture.detectChanges();

expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('ng-reflect-dir-prop="hello"');

fixture.componentInstance.ctxProp = null !;
fixture.detectChanges();

expect(getDOM().getInnerHTML(fixture.nativeElement)).not.toContain('ng-reflect-');
});

it('should reflect empty strings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);

fixture.componentInstance.ctxProp = '';
fixture.detectChanges();

expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-dir-prop=""');
});

it('should not reflect in comment nodes when the value changes to undefined', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp, {set: {template: `<ng-template [ngIf]="ctxBoolProp"></ng-template>`}})
.createComponent(MyComp);

fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();

let html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": "true"');

fixture.componentInstance.ctxBoolProp = undefined !;
fixture.detectChanges();

html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).not.toContain('ng-reflect');
});

it('should reflect in comment nodes when the value changes to null', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp, {set: {template: `<ng-template [ngIf]="ctxBoolProp"></ng-template>`}})
.createComponent(MyComp);

fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();

let html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": "true"');

fixture.componentInstance.ctxBoolProp = null !;
fixture.detectChanges();

html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": null');
});

});

describe('property decorators', () => {
Expand Down