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 fdb1daf

Browse files
author
vakrilov
committed
FIX: All events should execute inside zone
1 parent 1d7cd0f commit fdb1daf

File tree

5 files changed

+79
-29
lines changed

5 files changed

+79
-29
lines changed

‎nativescript-angular/renderer.ts‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Inject, Injectable, Optional} from '@angular/core/src/di';
1+
import {Inject, Injectable, Optional,NgZone} from '@angular/core';
22
import {
33
Renderer,
44
RootRenderer,
@@ -28,7 +28,9 @@ export class NativeScriptRootRenderer implements RootRenderer {
2828
constructor(
2929
@Optional() @Inject(APP_ROOT_VIEW) private _rootView: View,
3030
@Inject(DEVICE) device: Device,
31-
private _animationDriver: AnimationDriver) {
31+
private _animationDriver: AnimationDriver,
32+
private _zone: NgZone) {
33+
3234
this._viewUtil = new ViewUtil(device);
3335
}
3436

@@ -52,7 +54,7 @@ export class NativeScriptRootRenderer implements RootRenderer {
5254
renderComponent(componentProto: RenderComponentType): Renderer {
5355
let renderer = this._registeredComponents.get(componentProto.id);
5456
if (isBlank(renderer)) {
55-
renderer = new NativeScriptRenderer(this, componentProto, this._animationDriver);
57+
renderer = new NativeScriptRenderer(this, componentProto, this._animationDriver,this._zone);
5658
this._registeredComponents.set(componentProto.id, renderer);
5759
}
5860
return renderer;
@@ -63,16 +65,18 @@ export class NativeScriptRootRenderer implements RootRenderer {
6365
export class NativeScriptRenderer extends Renderer {
6466
private componentProtoId: string;
6567
private hasComponentStyles: boolean;
66-
private rootRenderer: NativeScriptRootRenderer;
6768

6869
private get viewUtil(): ViewUtil {
6970
return this.rootRenderer.viewUtil;
7071
}
7172

72-
constructor(private _rootRenderer: NativeScriptRootRenderer, private componentProto: RenderComponentType, private animationDriver: AnimationDriver) {
73+
constructor(
74+
private rootRenderer: NativeScriptRootRenderer,
75+
private componentProto: RenderComponentType,
76+
private animationDriver: AnimationDriver,
77+
private zone: NgZone) {
78+
7379
super();
74-
this.rootRenderer = _rootRenderer;
75-
let page = this.rootRenderer.page;
7680
let stylesLength = componentProto.styles.length;
7781
this.componentProtoId = componentProto.id;
7882
for (let i = 0; i < stylesLength; i++) {
@@ -93,7 +97,7 @@ export class NativeScriptRenderer extends Renderer {
9397
}
9498

9599
renderComponent(componentProto: RenderComponentType): Renderer {
96-
return this._rootRenderer.renderComponent(componentProto);
100+
return this.rootRenderer.renderComponent(componentProto);
97101
}
98102

99103
selectRootElement(selector: string): NgView {
@@ -211,7 +215,13 @@ export class NativeScriptRenderer extends Renderer {
211215

212216
public listen(renderElement: NgView, eventName: string, callback: Function): Function {
213217
traceLog('NativeScriptRenderer.listen: ' + eventName);
214-
let zonedCallback = (<any>global).Zone.current.wrap(callback);
218+
// Explicitly wrap in zone
219+
let zonedCallback = (...args) => {
220+
this.zone.run(() => {
221+
callback.apply(undefined, args);
222+
});
223+
};
224+
215225
renderElement.on(eventName, zonedCallback);
216226
if (eventName === View.loadedEvent && renderElement.isLoaded) {
217227
const notifyData = { eventName: View.loadedEvent, object: renderElement };

‎tests/app/tests/renderer-tests.ts‎

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//make sure you import mocha-config before @angular/core
22
import {assert} from "./test-config";
3-
import {Component, ElementRef, Renderer} from "@angular/core";
3+
import {Component, ElementRef, Renderer,NgZone} from "@angular/core";
44
import {ProxyViewContainer} from "ui/proxy-view-container";
55
import {Red} from "color/known-colors";
66
import {dumpView} from "./test-utils";
@@ -109,7 +109,8 @@ describe('Renderer E2E', () => {
109109
before(() => {
110110
return TestApp.create().then((app) => {
111111
testApp = app;
112-
})
112+
113+
});
113114
});
114115

115116
after(() => {
@@ -163,6 +164,44 @@ describe('Renderer E2E', () => {
163164
});
164165
});
165166

167+
it("executes events inside NgZone when listen is called inside NgZone", (done) => {
168+
const eventName = "someEvent";
169+
const view = new StackLayout();
170+
const evetArg = { eventName, object: view };
171+
const callback = (arg) => {
172+
assert.equal(arg, evetArg);
173+
assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone");
174+
done();
175+
};
176+
177+
testApp.zone.run(() => {
178+
testApp.renderer.listen(view, eventName, callback);
179+
});
180+
181+
setTimeout(() => {
182+
testApp.zone.runOutsideAngular(() => {
183+
view.notify(evetArg);
184+
});
185+
}, 10);
186+
});
187+
188+
it("executes events inside NgZone when listen is called outside NgZone", (done) => {
189+
const eventName = "someEvent";
190+
const view = new StackLayout();
191+
const evetArg = { eventName, object: view };
192+
const callback = (arg) => {
193+
assert.equal(arg, evetArg);
194+
assert.isTrue(NgZone.isInAngularZone(), "Event should be executed inside NgZone");
195+
done();
196+
};
197+
198+
testApp.zone.runOutsideAngular(() => {
199+
testApp.renderer.listen(view, eventName, callback);
200+
201+
view.notify(evetArg);
202+
});
203+
});
204+
166205
describe("Structural directives", () => {
167206
it("ngIf hides component when false", () => {
168207
return testApp.loadComponent(NgIfLabel).then((componentRef) => {
@@ -180,7 +219,7 @@ describe('Renderer E2E', () => {
180219
testApp.appRef.tick();
181220
assert.equal("(ProxyViewContainer (template), (Label))", dumpView(componentRoot));
182221
});
183-
})
222+
});
184223

185224
it("ngFor creates element for each item", () => {
186225
return testApp.loadComponent(NgForLabel).then((componentRef) => {
@@ -212,8 +251,8 @@ describe('Renderer E2E', () => {
212251
assert.equal("(ProxyViewContainer (template), (Label[text=one]), (Label[text=new]), (Label[text=two]), (Label[text=three]))", dumpView(componentRoot, true));
213252
});
214253
});
215-
})
216-
})
254+
});
255+
});
217256

218257
describe('Renderer createElement', () => {
219258
let testApp: TestApp = null;

‎tests/app/tests/style-properties.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ describe("Setting style properties", () => {
1414

1515
beforeEach(() => {
1616
const animationDriver = new NativeScriptAnimationDriver()
17-
const rootRenderer = new NativeScriptRootRenderer(null, device, animationDriver);
17+
const rootRenderer = new NativeScriptRootRenderer(null, device, animationDriver,null);
1818
const componentType = new RenderComponentType("id", "templateUrl", 0,
1919
null, []);
20-
renderer = new NativeScriptRenderer(rootRenderer, componentType, animationDriver);
20+
renderer = new NativeScriptRenderer(rootRenderer, componentType, animationDriver,null);
2121
element = <NgView><any>new TextField();
2222
});
2323

‎tests/app/tests/test-app.ts‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
//make sure you import mocha-config before @angular/core
22
import {bootstrap, ProviderArray} from "nativescript-angular/application";
33
import {Type, Component, ComponentRef, DynamicComponentLoader,
4-
ViewChild, ElementRef, provide, ApplicationRef, Renderer, ViewContainerRef
4+
ViewChild, ElementRef, provide, ApplicationRef, Renderer, ViewContainerRef,NgZone
55
} from "@angular/core";
66

7-
import {View} from "ui/core/view";
87
import {GridLayout} from "ui/layouts/grid-layout";
98
import {LayoutBase} from "ui/layouts/layout-base";
109
import {topmost} from 'ui/frame';
@@ -21,7 +20,8 @@ export class TestApp {
2120
constructor(public loader: DynamicComponentLoader,
2221
public elementRef: ViewContainerRef,
2322
public appRef: ApplicationRef,
24-
public renderer: Renderer) {
23+
public renderer: Renderer,
24+
public zone: NgZone) {
2525
}
2626

2727
public loadComponent(type: Type): Promise<ComponentRef<any>> {
@@ -49,7 +49,7 @@ export class TestApp {
4949
}
5050
}
5151

52-
var runningApps = new Map<any, { hostView: LayoutBase, appRoot: GridLayout, appRef: ApplicationRef }>();
52+
const runningApps = new Map<any, { hostView: LayoutBase, appRoot: GridLayout, appRef: ApplicationRef }>();
5353

5454
export function bootstrapTestApp<T>(appComponentType: new (...args) => T, providers: ProviderArray = []): Promise<T> {
5555
const page = topmost().currentPage;
@@ -61,17 +61,18 @@ export function bootstrapTestApp<T>(appComponentType: new (...args) => T, provid
6161
viewRoot.opacity = 0.7;
6262
GridLayout.setRowSpan(rootLayout, 50);
6363
GridLayout.setColumnSpan(rootLayout, 50);
64-
64+
6565
const rootViewProvider = provide(APP_ROOT_VIEW, { useValue: viewRoot });
6666
return bootstrap(appComponentType, providers.concat(rootViewProvider)).then((componentRef) => {
67-
componentRef.injector.get(ApplicationRef)
67+
componentRef.injector.get(ApplicationRef);
6868
const testApp = componentRef.instance;
69-
70-
runningApps.set(testApp, {
71-
hostView: rootLayout,
72-
appRoot: viewRoot,
73-
appRef: componentRef.injector.get(ApplicationRef) });
74-
69+
70+
runningApps.set(testApp, {
71+
hostView: rootLayout,
72+
appRoot: viewRoot,
73+
appRef: componentRef.injector.get(ApplicationRef)
74+
});
75+
7576
return testApp;
7677
});
7778
}

‎tests/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"version": "2.1.1"
66
},
77
"tns-ios": {
8-
"version": "2.0.1"
8+
"version": "2.1.1"
99
}
1010
},
1111
"name": "ngtests",

0 commit comments

Comments
(0)

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