1
- import { ComponentRef , ViewContainerRef , Component , Type , ViewChild , ComponentResolver } from '@angular/core' ;
1
+ import { ComponentRef , ViewContainerRef , Component , Type , ViewChild , ComponentResolver , ChangeDetectorRef , Host } from '@angular/core' ;
2
+ import trace = require( "trace" ) ;
2
3
3
4
type AnyComponentRef = ComponentRef < any > ;
4
5
interface PendingLoadEntry {
5
6
componentType : Type ;
6
7
resolveCallback : ( AnyComponentRef ) => void ;
7
8
}
8
9
10
+ export const CATEGORY = "detached-loader" ;
11
+ function log ( message : string ) {
12
+ trace . write ( message , CATEGORY ) ;
13
+ }
14
+
15
+
9
16
/**
10
17
* Wrapper component used for loading components when navigating
11
18
* It uses DetachedContainer as selector so that it is containerRef is not attached to the visual tree.
@@ -20,10 +27,11 @@ export class DetachedLoader {
20
27
private viewLoaded = false ;
21
28
private pendingLoads : PendingLoadEntry [ ] = [ ] ;
22
29
23
- constructor ( private compiler : ComponentResolver ) {
24
- }
30
+ constructor ( private compiler : ComponentResolver , private changeDetector : ChangeDetectorRef ) { }
25
31
26
32
public ngAfterViewInit ( ) {
33
+ log ( "DetachedLoader.ngAfterViewInit" ) ;
34
+
27
35
this . viewLoaded = true ;
28
36
this . pendingLoads . forEach ( loadEntry => {
29
37
this . loadInLocation ( loadEntry . componentType ) . then ( loadedRef => {
@@ -35,15 +43,30 @@ export class DetachedLoader {
35
43
private loadInLocation ( componentType : Type ) : Promise < ComponentRef < any > > {
36
44
return this . compiler . resolveComponent ( componentType ) . then ( ( componentFactory ) => {
37
45
return this . containerRef . createComponent ( componentFactory , this . containerRef . length , this . containerRef . parentInjector , null ) ;
38
- } ) ;
46
+ } ) . then ( ( compRef ) => {
47
+ log ( "DetachedLoader.loadInLocation component loaded -> markForCheck" ) ;
48
+ // Component is created, buit may not be checked if we are loading
49
+ // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us.
50
+ // We are inside a promise here so no need for setTimeout - CD should trigger after the promise.
51
+ this . changeDetector . markForCheck ( ) ;
52
+ return compRef ;
53
+ } )
39
54
}
40
55
41
56
public loadComponent ( componentType : Type ) : Promise < ComponentRef < any > > {
57
+ log ( "DetachedLoader.loadComponent viewLoaded: " + this . viewLoaded ) ;
58
+
42
59
// Check if called before placeholder is initialized.
43
60
// Delay load if so.
44
61
if ( this . viewLoaded ) {
45
62
return this . loadInLocation ( componentType ) ;
46
63
} else {
64
+ // loadComponent called, but detached-loader is still not initialized.
65
+ // Mark it for change and trigger change detection to be sure it will be initialized,
66
+ // so that loading can conitionue.
67
+ log ( "DetachedLoader.loadComponent -> markForCheck(with setTimeout())" )
68
+ setTimeout ( ( ) => this . changeDetector . markForCheck ( ) , 0 ) ;
69
+
47
70
return new Promise ( ( resolve , reject ) => {
48
71
this . pendingLoads . push ( {
49
72
componentType : componentType ,
0 commit comments