angular: For structural directives with empty attribute the corresponding @Input-method is not called
๐ bug report
Is this a regression?
Yes, this works at least in angular 7.1, 8.2.9
Description
When using a structural directive, but with an empty attribute, the corresponding setter for that property is not called. This might be intentional and not a bug, however it would then be an API-change which I did not find anything in the migration guide about and it is also inconsistent with the behaviour of non-structural directives.
๐ฌ Minimal Reproduction
The following example is a custom directive which uses *appShow
to show some content and *appShow="false"
to hide the content. The directive itself is of course rather useless, however it is the simplest showcase I came up with. The test fails on angular 9.0.0 and 9.0.2, but passes at least on 8.2.9 and on 7.1.0:
import { Component, DebugElement, Directive, Input, Predicate, TemplateRef, ViewContainerRef } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
@Component({
template: `
<div *appShow>
<p id="show">This should show</p>
</div>
<div *appShow="false">
<p id="noShow">This should hide</p>
</div>`
})
class TestShowDirectiveComponent {
}
@Directive({
selector: '[appShow]'
})
class ShowDirective {
constructor(private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) {}
@Input()
set appShow(show: boolean | '') {
if (show !== false) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
fdescribe('AppShowDirective', () => {
let fixture: ComponentFixture<TestShowDirectiveComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [TestShowDirectiveComponent, ShowDirective],
});
fixture = TestBed.createComponent(TestShowDirectiveComponent);
fixture.detectChanges();
});
function elementExists(selector: Predicate<DebugElement>): boolean {
return fixture.debugElement.queryAll(selector).length === 1;
}
it('should show only content without param', () => {
expect(elementExists(By.css('#show'))).toBe(true);
expect(elementExists(By.css('#noShow'))).toBe(false);
});
});
๐ Your Environment
Angular Version:
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ โณ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 9.0.2
Node: 12.15.0
OS: linux x64
Angular: 9.0.2
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, localize, platform-browser
... platform-browser-dynamic, router
Ivy Workspace: Yes
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.900.3
@angular-devkit/build-angular 0.900.3
@angular-devkit/build-optimizer 0.900.3
@angular-devkit/build-webpack 0.900.3
@angular-devkit/core 9.0.3
@angular-devkit/schematics 9.0.2
@ngtools/webpack 9.0.3
@schematics/angular 9.0.2
@schematics/update 0.900.2
rxjs 6.5.3
typescript 3.7.5
webpack 4.41.2
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 15 (7 by maintainers)
Commits related to this issue
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
- fix(compiler): set structural directive input as `null` when binding is empty Prior to this commit, inputs for structural directives that do not have a binding (e.g. `<input *dir>`) were not set, whi... — committed to AndrewKushnir/angular by AndrewKushnir 4 years ago
Iโve just upgraded a project from angular 8 to angular 10 and bumped into this issue. Took a while before finding this github isuse. I managed to work around the issue by just rebuilding the custom structural directive to always require an @Input parameter, which might help other people bumping into this issue as well.
However this is intended to be a temporary workaround, is there any news on when we can expect this bug to be fixed? And is there any way for me to support or help fix this issue? (@AndrewKushnir I saw you working on a PR to fix this?)
Hi @guzmanoj, the mentioned fix (that you refer to) was put on hold due to the fact that itโd slightly increase payload size of the generated code (more info needs to be included) and more investigation is needed to assess the impact. As a temporary solution, you can try adding the logic into either directiveโs constructor or use
ngOnInit
lifecycle hook and check for the case when directive was initialized, but no input provided.