From 9ba46d9f880bc561ad941f812532484f055c209c Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Thu, 19 Mar 2020 13:57:55 +0200 Subject: [PATCH] fix(elements): correctly handle setting inputs to `undefined` (#36140) Previously, when an input property was initially set to `undefined` it would not be correctly recognized as a change (and trigger `ngOnChanges()`). This commit ensures that explicitly setting an input to `undefined` is correctly handled the same as setting the property to any other value. This aligns the behavior of Angular custom elements with that of the corresponding components when used directly (not as custom elements). PR Close #36140 --- packages/elements/src/component-factory-strategy.ts | 6 +++++- packages/elements/test/component-factory-strategy_spec.ts | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/elements/src/component-factory-strategy.ts b/packages/elements/src/component-factory-strategy.ts index 6480045dae..fafe335b84 100644 --- a/packages/elements/src/component-factory-strategy.ts +++ b/packages/elements/src/component-factory-strategy.ts @@ -133,7 +133,11 @@ export class ComponentNgElementStrategy implements NgElementStrategy { return; } - if (strictEquals(value, this.getInputValue(property))) { + // Ignore the value if it is strictly equal to the current value, except if it is `undefined` + // and this is the first change to the value (because an explicit `undefined` _is_ strictly + // equal to not having a value set at all, but we still need to record this as a change). + if (strictEquals(value, this.getInputValue(property)) && + !((value === undefined) && this.unchangedInputs.has(property))) { return; } diff --git a/packages/elements/test/component-factory-strategy_spec.ts b/packages/elements/test/component-factory-strategy_spec.ts index 4a99800a6b..7887b55abf 100644 --- a/packages/elements/test/component-factory-strategy_spec.ts +++ b/packages/elements/test/component-factory-strategy_spec.ts @@ -94,6 +94,7 @@ describe('ComponentFactoryNgElementStrategy', () => { it('should call ngOnChanges with the change', () => { expectSimpleChanges(componentRef.instance.simpleChanges[0], { fooFoo: new SimpleChange(undefined, 'fooFoo-1', true), + falsyUndefined: new SimpleChange(undefined, undefined, true), falsyNull: new SimpleChange(undefined, null, true), falsyEmpty: new SimpleChange(undefined, '', true), falsyFalse: new SimpleChange(undefined, false, true), @@ -104,11 +105,13 @@ describe('ComponentFactoryNgElementStrategy', () => { it('should call ngOnChanges with proper firstChange value', fakeAsync(() => { strategy.setInputValue('fooFoo', 'fooFoo-2'); strategy.setInputValue('barBar', 'barBar-1'); + strategy.setInputValue('falsyUndefined', 'notanymore'); tick(16); // scheduler waits 16ms if RAF is unavailable (strategy as any).detectChanges(); expectSimpleChanges(componentRef.instance.simpleChanges[1], { fooFoo: new SimpleChange('fooFoo-1', 'fooFoo-2', false), barBar: new SimpleChange(undefined, 'barBar-1', true), + falsyUndefined: new SimpleChange(undefined, 'notanymore', false), }); })); });