angular: lazy load from ngComponentOutlet is broken after v12

Bug Report

Affected Package

The issue is caused by package @angular/core

Is this a regression?

Yes, the previous version in which this bug was not present was: v11

Description

We have this lazy components that loads using *ngComponentOutlet directive in v11 but after upgrade to v12, the same code didn’t worked as it displays errors like Can't bind to 'ngIf' since it isn't a known property or The pipe 'async' could not be found! etc., I think this is because of the common module isn’t get referenced.

Minimal Reproduction

I have created this stackblitz repo so just open the console and you’ll see the issues.

If you downgrade it into v11, the same code works.

// alzy-two component

@Component({
  selector: 'app-lazy-2',
  template: `
    <h4>loading lazy level 2</h4>
  `
})
export class LazyTwoComponent {}

// lazy-two module
import { LazyTwoComponent } from './lazy-two.component';

@NgModule({
  imports: [CommonModule],
  declarations: [LazyTwoComponent]
})
export class LazyTwoModule {}

// lazy one

import { LazyTwoComponent } from '../lazy2/lazy-two.component';

@Component({
  selector: 'app-lazy-1',
  template: `
    <h1 *ngIf="title">{{title}}</h1>
    <ng-template [ngIf]="lazyComp | async">
     <ng-container *ngComponentOutlet="lazyComp | async"></ng-container>
    </ng-template>
  `
})
export class LazyOneComponent implements AfterViewInit {
  title = 'hello from one'
  lazyComp: Promise<Type<LazyTwoComponent>>
  ngAfterViewInit(){
    this.lazyComp = import('../lazy2/lazy-two.component')
    .then(({LazyTwoComponent})=> LazyTwoComponent)
  }
}

// lazy-one.module

import { LazyOneComponent } from './lazy-one.component';

@NgModule({
  imports: [CommonModule],
  declarations: [LazyOneComponent]
})
export class LazOneModule {}

// app.ts

import { LazyOneComponent } from './components/lazy1/lazy-one.component';

@Component({
  selector: 'my-app',
  template`<ng-container *ngComponentOutlet="lazyComp | async"></ng-container>`
})
export class AppComponent implements AfterViewInit  {

  lazyComp: Promise<Type<LazyOneComponent>>

  ngAfterViewInit(){
    this.lazyComp = import('./components/lazy1/lazy-one.component').then(({LazyOneComponent}) => LazyOneComponent)
  }
}

// app.module

import { AppComponent } from './app.component';

@NgModule({
  imports:      [CommonModule, BrowserModule],
  declarations: [ AppComponent],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Exception or Error



NG0303: Can't bind to 'ngIf' since it isn't a known property of 'h1'.
NG0302: The pipe 'async' could not be found!.

Your Environment

Angular Version:



@angular-devkit/architect       0.1201.0
@angular-devkit/build-angular   12.1.0
@angular-devkit/core            12.1.0
@angular-devkit/schematics      12.1.0
@schematics/angular             12.1.0
rxjs                            6.6.7
typescript                      4.3.4

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (13 by maintainers)

Most upvoted comments

@irowbin Sure, but the main point is that the Angular compiler must understand that such a module should be taken into account. That’s why there should be something that did it in your case, and it’s not clear what it was.

There is absolutely no need to declare the component and NgModule in the same file. As long as the NgModule class is imported somewhere, you’ll be fine.

The way it works is that the Angular compiler analyzes your NgModules and figures out what components or directives are available to the component in its module scope. Once it has this data, it adds it to the component definition.

Netanel Basal’s article uses the variant of a single file for declaring a component and its module.

@irowbin The logic is the same in v11 and v12. As I know, there is no difference between both versions.

This appears to be working as expected. If the lazy-one.module.ts file is never imported from anywhere, the compiler won’t be aware of its existence depending on the TypeScript configuration. You’ll typically see something like "files": ["src/index.ts"] in tsconfig.app.json which means that a single file is included which will then pull in additional files as they are discovered through import statements. I suspect that in v11 this was configured differently, e.g. using an includes rule in tsconfig.app.json, such that the lazy-one.module.ts was part of the compilation and therefore made the list of declarations available to LazyOneComponent.