Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a85b1ee

Browse files
author
Alexander Vakrilov
committed
Merge pull request #274 from NativeScript/detached-loader-change-detection
Detached loader change detection
2 parents 814a8b4 + ba324cd commit a85b1ee

File tree

2 files changed

+132
-4
lines changed

2 files changed

+132
-4
lines changed

‎nativescript-angular/common/detached-loader.ts‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
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");
23

34
type AnyComponentRef = ComponentRef<any>;
45
interface PendingLoadEntry {
56
componentType: Type;
67
resolveCallback: (AnyComponentRef) => void;
78
}
89

10+
export const CATEGORY = "detached-loader";
11+
function log(message: string) {
12+
trace.write(message, CATEGORY);
13+
}
14+
15+
916
/**
1017
* Wrapper component used for loading components when navigating
1118
* It uses DetachedContainer as selector so that it is containerRef is not attached to the visual tree.
@@ -20,10 +27,11 @@ export class DetachedLoader {
2027
private viewLoaded = false;
2128
private pendingLoads: PendingLoadEntry[] = [];
2229

23-
constructor(private compiler: ComponentResolver) {
24-
}
30+
constructor(private compiler: ComponentResolver, private changeDetector: ChangeDetectorRef) { }
2531

2632
public ngAfterViewInit() {
33+
log("DetachedLoader.ngAfterViewInit");
34+
2735
this.viewLoaded = true;
2836
this.pendingLoads.forEach(loadEntry => {
2937
this.loadInLocation(loadEntry.componentType).then(loadedRef => {
@@ -35,15 +43,30 @@ export class DetachedLoader {
3543
private loadInLocation(componentType: Type): Promise<ComponentRef<any>> {
3644
return this.compiler.resolveComponent(componentType).then((componentFactory) => {
3745
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+
})
3954
}
4055

4156
public loadComponent(componentType: Type): Promise<ComponentRef<any>> {
57+
log("DetachedLoader.loadComponent viewLoaded: " + this.viewLoaded);
58+
4259
// Check if called before placeholder is initialized.
4360
// Delay load if so.
4461
if (this.viewLoaded) {
4562
return this.loadInLocation(componentType);
4663
} 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+
4770
return new Promise((resolve, reject) => {
4871
this.pendingLoads.push({
4972
componentType: componentType,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//make sure you import mocha-config before @angular/core
2+
import {assert} from "./test-config";
3+
import {Component, ElementRef, Renderer, AfterViewInit, OnInit, ViewChild, ChangeDetectionStrategy} from "@angular/core";
4+
import {ProxyViewContainer} from "ui/proxy-view-container";
5+
import {Red} from "color/known-colors";
6+
import {dumpView} from "./test-utils";
7+
import {TestApp} from "./test-app";
8+
import {LayoutBase} from "ui/layouts/layout-base";
9+
import {StackLayout} from "ui/layouts/stack-layout";
10+
import {DetachedLoader} from "nativescript-angular/common/detached-loader";
11+
12+
@Component({
13+
template: `<StackLayout><Label text="COMPONENT"></Label></StackLayout>`
14+
})
15+
class TestComponent { }
16+
17+
18+
class LoaderComponentBase {
19+
@ViewChild(DetachedLoader) public loader: DetachedLoader;
20+
21+
public ready: Promise<LoaderComponent>;
22+
private resolve;
23+
constructor() {
24+
this.ready = new Promise((reslove, reject) => {
25+
this.resolve = reslove;
26+
})
27+
}
28+
ngAfterViewInit() {
29+
this.resolve(this);
30+
}
31+
}
32+
33+
@Component({
34+
selector: 'loader-component-on-push',
35+
directives: [DetachedLoader],
36+
template: `
37+
<StackLayout>
38+
<DetachedContainer #loader></DetachedContainer>
39+
</StackLayout>
40+
`
41+
})
42+
export class LoaderComponent extends LoaderComponentBase { }
43+
44+
@Component({
45+
selector: 'loader-component-on-push',
46+
directives: [DetachedLoader],
47+
changeDetection: ChangeDetectionStrategy.OnPush,
48+
template: `
49+
<StackLayout>
50+
<DetachedContainer #loader></DetachedContainer>
51+
</StackLayout>
52+
`
53+
})
54+
export class LoaderComponentOnPush extends LoaderComponentBase { }
55+
56+
describe ('DetachedLoader', () => {
57+
let testApp: TestApp = null;
58+
59+
before(() => {
60+
return TestApp.create().then((app) => {
61+
testApp = app;
62+
})
63+
});
64+
65+
after(() => {
66+
testApp.dispose();
67+
});
68+
69+
afterEach(() => {
70+
testApp.disposeComponents();
71+
});
72+
73+
it("creates component", (done) => {
74+
testApp.loadComponent(LoaderComponent)
75+
.then((componentRef) => {
76+
// wait for the ngAfterViewInit
77+
return (<LoaderComponent>componentRef.instance).ready;
78+
})
79+
.then((comp) => {
80+
// load test component with loader
81+
return comp.loader.loadComponent(TestComponent);
82+
})
83+
.then((compRef) => {
84+
done();
85+
})
86+
.catch(done);
87+
});
88+
89+
90+
it("creates component when ChangeDetectionStrategy is OnPush", (done) => {
91+
testApp.loadComponent(LoaderComponentOnPush)
92+
.then((componentRef) => {
93+
// wait for the ngAfterViewInit
94+
return (<LoaderComponentOnPush>componentRef.instance).ready;
95+
})
96+
.then((comp) => {
97+
// load test component with loader
98+
return comp.loader.loadComponent(TestComponent);
99+
})
100+
.then((compRef) => {
101+
done();
102+
})
103+
.catch(done);
104+
});
105+
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /