Description
🐞 bug report
Is this a regression?
Yes, in angular 8 this works fine. But after an update to angular 9 it stops working. If I opt-out ivy in the tsconfig.app.json ("enableIvy": false) it is working again.
Description
The problem occurs if a parent directive is injected with the @host annotation in nested structures. So the first injection works fine. But all injections in deeper nested structers fails and null is injected.
In the root component a ui property is defined which contains nested child elements.
@Component({
selector: 'app-root',
template: `
<app-container [element]="ui"></app-container>
`
})
export class AppComponent {
ui: ElementDef = {
name: 'container1', child: {
name: 'container2', child: {
name: 'container3', child: {
name: 'container4'
}
}
}
};
}
The elements are rendered by the container component. Each rendered element is embedded in a div with a directive "appContainerWrapper" attached.
The parent directive of a container is injected in the constructor with the @host annotation.
@Component({
selector: 'app-container',
template: `
<div appContainerWrapper *ngIf="element">
<p>Container: {{element.name}}</p>
<app-container [element]="element.child"></app-container>
</div>
`
})
export class ContainerComponent implements OnInit {
@Input() element: ElementDef;
constructor(@Optional() @Host() private directive: ContainerWrapperDirective) {}
[...]
ngOnInit(): void {
if (this.directive !== null) {
this.directive.update(color);
} else {
// in nested containers this.directive is null if ivy is enabled :(
}
}
}
The directive should set the background color of the parent element if update
is called from the child element.
@Directive({
selector: '[appContainerWrapper]'
})
export class ContainerWrapperDirective {
constructor(private containerWrapperRef: ElementRef, private renderer: Renderer2) {}
update(color: string): void {
const nativeElement = this.containerWrapperRef.nativeElement;
this.renderer.setStyle(nativeElement, 'background-color', color);
}
}
🔬 Minimal Reproduction
The problem cannot be reproduced in stackblitz. I therefor created a sample application on Github
The repository contains two branches:
-
master: Angular 9 with ivy enabled --> the parent directive is only injected in the first container where a directive is available. In all further containers the injected directive is null. Therefor only one "background is set".
-
opt-out-ivy: same as the master branch but set "enableIvy" in tsconfig.app.json to false --> everything works as expected as with angular 8. In each container the parent directive is injected.
🌍 Your Environment
Angular Version:
Angular CLI: 9.0.7
Node: 12.16.1
OS: win32 x64
Angular: 9.0.7
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.900.7
@angular-devkit/build-angular 0.900.7
@angular-devkit/build-optimizer 0.900.7
@angular-devkit/build-webpack 0.900.7
@angular-devkit/core 9.0.7
@angular-devkit/schematics 9.0.7
@ngtools/webpack 9.0.7
@schematics/angular 9.0.7
@schematics/update 0.900.7
rxjs 6.5.4
typescript 3.7.5
webpack 4.41.2