Skip to content

Commit

Permalink
feat: Angular 4.2 support (#842)
Browse files Browse the repository at this point in the history
* 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,
    ]
…
})
  • Loading branch information
sis0k0 authored Jul 31, 2017
1 parent d6aab55 commit eb3fd81
Show file tree
Hide file tree
Showing 31 changed files with 3,021 additions and 434 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ tests/test-output.txt
tests/platforms
tests/lib
tests/node_modules
tests/hooks

ng-sample/app/**/*.js
ng-sample/app/global.d.ts
ng-sample/platforms
ng-sample/lib
ng-sample/node_modules
ng-sample/hooks
ng-sample/app/nativescript-angular

startup-test/platforms
Expand All @@ -52,4 +54,4 @@ startup-test/node_modules
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/extensions.json
22 changes: 14 additions & 8 deletions nativescript-angular/animations.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { NgModule, Injectable, NgZone, Provider, RendererFactory2 } from "@angular/core";

import { AnimationBuilder } from "@angular/animations";

import {
AnimationDriver,
ɵAnimationEngine as AnimationEngine,
ɵAnimationStyleNormalizer as AnimationStyleNormalizer,
ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer
ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer,
} from "@angular/animations/browser";

import { ɵAnimationRendererFactory as AnimationRendererFactory } from "@angular/platform-browser/animations";
import {
ɵAnimationRendererFactory as AnimationRendererFactory,
ɵBrowserAnimationBuilder as BrowserAnimationBuilder,
} from "@angular/platform-browser/animations";

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

export function instantiateRendererFactory(
renderer: NativeScriptRendererFactory, engine: AnimationEngine, zone: NgZone) {
renderer: NativeScriptRendererFactory, engine: NativeScriptAnimationEngine, zone: NgZone) {
return new AnimationRendererFactory(renderer, engine, zone);
}

export function instanciateDefaultStyleNormalizer() {
export function instantiateDefaultStyleNormalizer() {
return new WebAnimationsStyleNormalizer();
}

export const NATIVESCRIPT_ANIMATIONS_PROVIDERS: Provider[] = [
{provide: AnimationBuilder, useClass: BrowserAnimationBuilder},
{provide: AnimationDriver, useFactory: instantiateSupportedAnimationDriver},
{provide: AnimationStyleNormalizer, useFactory: instanciateDefaultStyleNormalizer},
{provide: AnimationEngine, useClass: InjectableAnimationEngine}, {
{provide: AnimationStyleNormalizer, useFactory: instantiateDefaultStyleNormalizer},
{provide: NativeScriptAnimationEngine, useClass: InjectableAnimationEngine},
{
provide: RendererFactory2,
useFactory: instantiateRendererFactory,
deps: [NativeScriptRendererFactory, AnimationEngine, NgZone]
deps: [NativeScriptRendererFactory, NativeScriptAnimationEngine, NgZone]
}
];

Expand Down
180 changes: 169 additions & 11 deletions nativescript-angular/animations/animation-driver.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,117 @@
import { AnimationPlayer } from "@angular/animations";
import { AnimationDriver } from "@angular/animations/browser";
import { eachDescendant } from "tns-core-modules/ui/core/view";

import { NgView } from "../element-registry";
import { NativeScriptAnimationPlayer } from "./animation-player";
import { Keyframe } from "./utils";
import {
Keyframe,
dashCaseToCamelCase,
} from "./utils";
import { NgView, InvisibleNode } from "../element-registry";
import { animationsLog as traceLog } from "../trace";

export abstract class AnimationDriver {
abstract animate(
element: any,
keyframes: Keyframe[],
duration: number,
delay: number,
easing: string
): AnimationPlayer;
import { createSelector, SelectorCore } from "tns-core-modules/ui/styling/css-selector";

interface ViewMatchResult {
found: boolean;
}

interface ViewMatchParams {
originalView: NgView;
}

interface QueryParams {
selector: Selector;
multi: boolean;
}

interface QueryResult {
matches: NgView[];
}

class Selector {
private nsSelectors: SelectorCore[];
private classSelectors: string[];

constructor(rawSelector: string) {
this.parse(rawSelector);
}

match(element: NgView): boolean {
return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
}

private parse(rawSelector: string) {
const selectors = rawSelector.split(",").map(s => s.trim());

this.nsSelectors = selectors.map(createSelector);
this.classSelectors = selectors
.filter(s => s.startsWith("."))
.map(s => s.substring(1));
}

private nsSelectorMatch(element: NgView) {
return this.nsSelectors.some(s => s.match(element));
}

private classSelectorsMatch(element: NgView) {
return this.classSelectors.some(s => this.hasClass(element, s));
}

// we're using that instead of match for classes
// that are dynamically added by the animation engine
// such as .ng-trigger, that's added for every :enter view
private hasClass(element: NgView, cls: string) {
return element && element["$$classes"] && element["$$classes"][cls];
}
}

export class NativeScriptAnimationDriver implements AnimationDriver {
matchesElement(element: NgView, rawSelector: string): boolean {
traceLog(
`NativeScriptAnimationDriver.matchesElement ` +
`element: ${element}, selector: ${rawSelector}`
);

const selector = this.makeSelector(rawSelector);
return selector.match(element);
}


containsElement(elm1: NgView, elm2: NgView): boolean {
traceLog(
`NativeScriptAnimationDriver.containsElement ` +
`element1: ${elm1}, element2: ${elm2}`
);

const params: ViewMatchParams = { originalView: elm2 };
const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);

return result.found;
}

query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
traceLog(
`NativeScriptAnimationDriver.query ` +
`element: ${element}, selector: ${rawSelector} ` +
`multi: ${multi}`
);

const selector = this.makeSelector(rawSelector);
const params: QueryParams = { selector, multi };
const result: QueryResult = this.visitDescendants(element, queryDescendants, params);

return result.matches || [];
}

computeStyle(element: NgView, prop: string): string {
return element.style[`css-${prop}`];
traceLog(
`NativeScriptAnimationDriver.computeStyle ` +
`element: ${element}, prop: ${prop}`
);

const camelCaseProp = dashCaseToCamelCase(prop);
return element.style[camelCaseProp];
}

animate(
Expand All @@ -26,7 +121,70 @@ export class NativeScriptAnimationDriver implements AnimationDriver {
delay: number,
easing: string
): AnimationPlayer {
traceLog(
`NativeScriptAnimationDriver.animate ` +
`element: ${element}, keyframes: ${keyframes} ` +
`duration: ${duration}, delay: ${delay} ` +
`easing: ${easing}`
);

return new NativeScriptAnimationPlayer(
element, keyframes, duration, delay, easing);
}

private makeSelector(rawSelector: string): Selector {
return new Selector(rawSelector);
}

private visitDescendants(
element: NgView,
cb: (child: NgView, result: any, params: any) => boolean,
cbParams: any): any {

const result = {};
// fill the result obj with the result from the callback function
eachDescendant(element, (child: NgView) => cb(child, result, cbParams));

return result;
}
}

function viewMatches(
element: NgView,
result: ViewMatchResult,
params: ViewMatchParams
): boolean {

if (element === params.originalView) {
result.found = true;
}

return !result.found;
}

function queryDescendants(
element: NgView,
result: QueryResult,
params: QueryParams
): boolean {

if (!result.matches) {
result.matches = [];
}

const { selector, multi } = params;

// skip comment and text nodes
// because they are not actual Views
// and cannot be animated
if (element instanceof InvisibleNode) {
return true;
}

if (selector.match(element)) {
result.matches.push(element);
return multi;
}

return true;
}
Loading

1 comment on commit eb3fd81

@vchimev
Copy link
Contributor

@vchimev vchimev commented on eb3fd81 Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rosen-vladimirov likes this!

Please sign in to comment.