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

Most upvoted comments

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.