Skip to content

Commit eb3fd81

Browse files
authored
feat: Angular 4.2 support (#842)
* feat: reimplement NativeScriptAnimationsModule to be compliant with the new NG Animations' API * fix(page-router-outlet): don't run change detection every time an outlet is activated * refactor: separate NativeScriptCommonModule from NativeScriptModule All common providers, such as CommonModule (from @angular/common), Frame, Page, Device and Modals are moved from NativeScriptModule to NativeScriptCommonModule. The NativeScript module now provides only essential parts such as Renderer and ModuleLoader. It also re-exports the NativeScriptCommonModule. It can be required from "nativescript-angular/common". The NativeScriptRouterModule also Reason: Importing NativeScriptModule more than once causes reinstantiating of the NativeScriptRenderer, which breaks animations. BREAKING CHANGES: NativeScriptModule should be imported only in the root application module (usually named AppModule). ApplicationModule. All other NgModules in the app (both feature and lazy-loaded ones) should import the NativeScriptCommonModule instead. The behaviour is alligned with BrowserModule and CommonModule in web Angular apps. angular.io/guide/ngmodule-faq#q-browser-vs-common-module Migration steps: In all NgModules, instead of the root one, replace: ``` import { NativeScriptModule } from "nativescript-angular/nativescript.module"; … @NgModule({ imports: [ NativeScriptModule, ] … }) ``` with: ``` import { NativeScriptCommonModule } from "nativescript-angular/common"; … @NgModule({ imports: [ NativeScriptCommonModule, ] … })
1 parent d6aab55 commit eb3fd81

31 files changed

+3021
-434
lines changed

Diff for: .gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ tests/test-output.txt
2727
tests/platforms
2828
tests/lib
2929
tests/node_modules
30+
tests/hooks
3031

3132
ng-sample/app/**/*.js
3233
ng-sample/app/global.d.ts
3334
ng-sample/platforms
3435
ng-sample/lib
3536
ng-sample/node_modules
37+
ng-sample/hooks
3638
ng-sample/app/nativescript-angular
3739

3840
startup-test/platforms
@@ -52,4 +54,4 @@ startup-test/node_modules
5254
!.vscode/settings.json
5355
!.vscode/tasks.json
5456
!.vscode/launch.json
55-
!.vscode/extensions.json
57+
!.vscode/extensions.json

Diff for: nativescript-angular/animations.ts

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { NgModule, Injectable, NgZone, Provider, RendererFactory2 } from "@angular/core";
22

3+
import { AnimationBuilder } from "@angular/animations";
4+
35
import {
46
AnimationDriver,
5-
ɵAnimationEngine as AnimationEngine,
67
ɵAnimationStyleNormalizer as AnimationStyleNormalizer,
7-
ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer
8+
ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer,
89
} from "@angular/animations/browser";
910

10-
import { ɵAnimationRendererFactory as AnimationRendererFactory } from "@angular/platform-browser/animations";
11+
import {
12+
ɵAnimationRendererFactory as AnimationRendererFactory,
13+
ɵBrowserAnimationBuilder as BrowserAnimationBuilder,
14+
} from "@angular/platform-browser/animations";
1115

1216
import { NativeScriptAnimationEngine } from "./animations/animation-engine";
1317
import { NativeScriptAnimationDriver } from "./animations/animation-driver";
@@ -26,21 +30,23 @@ export function instantiateSupportedAnimationDriver() {
2630
}
2731

2832
export function instantiateRendererFactory(
29-
renderer: NativeScriptRendererFactory, engine: AnimationEngine, zone: NgZone) {
33+
renderer: NativeScriptRendererFactory, engine: NativeScriptAnimationEngine, zone: NgZone) {
3034
return new AnimationRendererFactory(renderer, engine, zone);
3135
}
3236

33-
export function instanciateDefaultStyleNormalizer() {
37+
export function instantiateDefaultStyleNormalizer() {
3438
return new WebAnimationsStyleNormalizer();
3539
}
3640

3741
export const NATIVESCRIPT_ANIMATIONS_PROVIDERS: Provider[] = [
42+
{provide: AnimationBuilder, useClass: BrowserAnimationBuilder},
3843
{provide: AnimationDriver, useFactory: instantiateSupportedAnimationDriver},
39-
{provide: AnimationStyleNormalizer, useFactory: instanciateDefaultStyleNormalizer},
40-
{provide: AnimationEngine, useClass: InjectableAnimationEngine}, {
44+
{provide: AnimationStyleNormalizer, useFactory: instantiateDefaultStyleNormalizer},
45+
{provide: NativeScriptAnimationEngine, useClass: InjectableAnimationEngine},
46+
{
4147
provide: RendererFactory2,
4248
useFactory: instantiateRendererFactory,
43-
deps: [NativeScriptRendererFactory, AnimationEngine, NgZone]
49+
deps: [NativeScriptRendererFactory, NativeScriptAnimationEngine, NgZone]
4450
}
4551
];
4652

Diff for: nativescript-angular/animations/animation-driver.ts

+169-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,117 @@
11
import { AnimationPlayer } from "@angular/animations";
2+
import { AnimationDriver } from "@angular/animations/browser";
3+
import { eachDescendant } from "tns-core-modules/ui/core/view";
24

3-
import { NgView } from "../element-registry";
45
import { NativeScriptAnimationPlayer } from "./animation-player";
5-
import { Keyframe } from "./utils";
6+
import {
7+
Keyframe,
8+
dashCaseToCamelCase,
9+
} from "./utils";
10+
import { NgView, InvisibleNode } from "../element-registry";
11+
import { animationsLog as traceLog } from "../trace";
612

7-
export abstract class AnimationDriver {
8-
abstract animate(
9-
element: any,
10-
keyframes: Keyframe[],
11-
duration: number,
12-
delay: number,
13-
easing: string
14-
): AnimationPlayer;
13+
import { createSelector, SelectorCore } from "tns-core-modules/ui/styling/css-selector";
14+
15+
interface ViewMatchResult {
16+
found: boolean;
17+
}
18+
19+
interface ViewMatchParams {
20+
originalView: NgView;
21+
}
22+
23+
interface QueryParams {
24+
selector: Selector;
25+
multi: boolean;
26+
}
27+
28+
interface QueryResult {
29+
matches: NgView[];
30+
}
31+
32+
class Selector {
33+
private nsSelectors: SelectorCore[];
34+
private classSelectors: string[];
35+
36+
constructor(rawSelector: string) {
37+
this.parse(rawSelector);
38+
}
39+
40+
match(element: NgView): boolean {
41+
return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
42+
}
43+
44+
private parse(rawSelector: string) {
45+
const selectors = rawSelector.split(",").map(s => s.trim());
46+
47+
this.nsSelectors = selectors.map(createSelector);
48+
this.classSelectors = selectors
49+
.filter(s => s.startsWith("."))
50+
.map(s => s.substring(1));
51+
}
52+
53+
private nsSelectorMatch(element: NgView) {
54+
return this.nsSelectors.some(s => s.match(element));
55+
}
56+
57+
private classSelectorsMatch(element: NgView) {
58+
return this.classSelectors.some(s => this.hasClass(element, s));
59+
}
60+
61+
// we're using that instead of match for classes
62+
// that are dynamically added by the animation engine
63+
// such as .ng-trigger, that's added for every :enter view
64+
private hasClass(element: NgView, cls: string) {
65+
return element && element["$$classes"] && element["$$classes"][cls];
66+
}
1567
}
1668

1769
export class NativeScriptAnimationDriver implements AnimationDriver {
70+
matchesElement(element: NgView, rawSelector: string): boolean {
71+
traceLog(
72+
`NativeScriptAnimationDriver.matchesElement ` +
73+
`element: ${element}, selector: ${rawSelector}`
74+
);
75+
76+
const selector = this.makeSelector(rawSelector);
77+
return selector.match(element);
78+
}
79+
80+
81+
containsElement(elm1: NgView, elm2: NgView): boolean {
82+
traceLog(
83+
`NativeScriptAnimationDriver.containsElement ` +
84+
`element1: ${elm1}, element2: ${elm2}`
85+
);
86+
87+
const params: ViewMatchParams = { originalView: elm2 };
88+
const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
89+
90+
return result.found;
91+
}
92+
93+
query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
94+
traceLog(
95+
`NativeScriptAnimationDriver.query ` +
96+
`element: ${element}, selector: ${rawSelector} ` +
97+
`multi: ${multi}`
98+
);
99+
100+
const selector = this.makeSelector(rawSelector);
101+
const params: QueryParams = { selector, multi };
102+
const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
103+
104+
return result.matches || [];
105+
}
106+
18107
computeStyle(element: NgView, prop: string): string {
19-
return element.style[`css-${prop}`];
108+
traceLog(
109+
`NativeScriptAnimationDriver.computeStyle ` +
110+
`element: ${element}, prop: ${prop}`
111+
);
112+
113+
const camelCaseProp = dashCaseToCamelCase(prop);
114+
return element.style[camelCaseProp];
20115
}
21116

22117
animate(
@@ -26,7 +121,70 @@ export class NativeScriptAnimationDriver implements AnimationDriver {
26121
delay: number,
27122
easing: string
28123
): AnimationPlayer {
124+
traceLog(
125+
`NativeScriptAnimationDriver.animate ` +
126+
`element: ${element}, keyframes: ${keyframes} ` +
127+
`duration: ${duration}, delay: ${delay} ` +
128+
`easing: ${easing}`
129+
);
130+
29131
return new NativeScriptAnimationPlayer(
30132
element, keyframes, duration, delay, easing);
31133
}
134+
135+
private makeSelector(rawSelector: string): Selector {
136+
return new Selector(rawSelector);
137+
}
138+
139+
private visitDescendants(
140+
element: NgView,
141+
cb: (child: NgView, result: any, params: any) => boolean,
142+
cbParams: any): any {
143+
144+
const result = {};
145+
// fill the result obj with the result from the callback function
146+
eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
147+
148+
return result;
149+
}
150+
}
151+
152+
function viewMatches(
153+
element: NgView,
154+
result: ViewMatchResult,
155+
params: ViewMatchParams
156+
): boolean {
157+
158+
if (element === params.originalView) {
159+
result.found = true;
160+
}
161+
162+
return !result.found;
163+
}
164+
165+
function queryDescendants(
166+
element: NgView,
167+
result: QueryResult,
168+
params: QueryParams
169+
): boolean {
170+
171+
if (!result.matches) {
172+
result.matches = [];
173+
}
174+
175+
const { selector, multi } = params;
176+
177+
// skip comment and text nodes
178+
// because they are not actual Views
179+
// and cannot be animated
180+
if (element instanceof InvisibleNode) {
181+
return true;
182+
}
183+
184+
if (selector.match(element)) {
185+
result.matches.push(element);
186+
return multi;
187+
}
188+
189+
return true;
32190
}

0 commit comments

Comments
 (0)