From c5a93fa336f19be95c5c52afa30f0b1a5053e347 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Wed, 8 Jun 2016 17:37:09 +0300 Subject: [PATCH 1/6] Updated async examples --- ng-sample/app/app.ts | 7 +- ng-sample/app/examples/list/data.service.ts | 54 +++++++ .../app/examples/list/list-test-async.css | 15 +- .../app/examples/list/list-test-async.ts | 133 +++++++++++++----- .../nativescript-angular-sync .js | 2 +- 5 files changed, 169 insertions(+), 42 deletions(-) create mode 100644 ng-sample/app/examples/list/data.service.ts diff --git a/ng-sample/app/app.ts b/ng-sample/app/app.ts index 3659877ae..5bffbac75 100644 --- a/ng-sample/app/app.ts +++ b/ng-sample/app/app.ts @@ -18,7 +18,7 @@ import {RendererTest} from './examples/renderer-test'; import {TabViewTest} from './examples/tab-view/tab-view-test'; import {Benchmark} from './performance/benchmark'; import {ListTest} from './examples/list/list-test'; -import {ListTestAsync} from "./examples/list/list-test-async"; +import {ListTestAsync, ListTestFilterAsync} from "./examples/list/list-test-async"; import {ImageTest} from "./examples/image/image-test"; import {NavigationTest} from "./examples/navigation/navigation-test"; import {ActionBarTest} from "./examples/action-bar/action-bar-test"; @@ -31,7 +31,8 @@ import {LoginTest} from "./examples/navigation/login-test"; //nativeScriptBootstrap(TabViewTest); //nativeScriptBootstrap(Benchmark); //nativeScriptBootstrap(ListTest); -//nativeScriptBootstrap(ListTestAsync); +// nativeScriptBootstrap(ListTestAsync); +nativeScriptBootstrap(ListTestFilterAsync); //nativeScriptBootstrap(ImageTest); //nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS]); //nativeScriptBootstrap(ActionBarTest, [NS_ROUTER_PROVIDERS], { startPageActionBarHidden: false }); @@ -39,4 +40,4 @@ import {LoginTest} from "./examples/navigation/login-test"; //nativeScriptBootstrap(ModalTest); //nativeScriptBootstrap(PlatfromDirectivesTest); //nativeScriptBootstrap(RouterOutletTest, [NS_ROUTER_PROVIDERS]); -nativeScriptBootstrap(LoginTest, [NS_ROUTER_PROVIDERS]); +// nativeScriptBootstrap(LoginTest, [NS_ROUTER_PROVIDERS]); diff --git a/ng-sample/app/examples/list/data.service.ts b/ng-sample/app/examples/list/data.service.ts new file mode 100644 index 000000000..e7139138b --- /dev/null +++ b/ng-sample/app/examples/list/data.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from "rxjs/BehaviorSubject"; + +export class DataItem { + constructor(public id: number, public name: string) { } +} + +@Injectable() +export class DataService { + private _intervalId; + private _counter = 0; + private _currentItems: Array; + + public items$: BehaviorSubject>; + + constructor() { + this._currentItems = []; + for (var i = 0; i < 3; i++) { + this.appendItem() + } + + this.items$ = new BehaviorSubject(this._currentItems); + } + + public startAsyncUpdates() { + if (this._intervalId) { + throw new Error("Updates are already started"); + } + + this._intervalId = setInterval(() => { + this.appendItem(); + this.publishUpdates(); + }, 200); + + } + + public stopAsyncUpdates() { + if (!this._intervalId) { + throw new Error("Updates are not started"); + } + + clearInterval(this._intervalId); + this._intervalId = undefined; + } + + private publishUpdates() { + this.items$.next([...this._currentItems]); + } + + private appendItem() { + this._currentItems.push(new DataItem(this._counter, "data item " + this._counter)); + this._counter++; + } +} \ No newline at end of file diff --git a/ng-sample/app/examples/list/list-test-async.css b/ng-sample/app/examples/list/list-test-async.css index 8853405b7..c3c993dd4 100644 --- a/ng-sample/app/examples/list/list-test-async.css +++ b/ng-sample/app/examples/list/list-test-async.css @@ -1,3 +1,16 @@ -.test { +button { + margin: 0 10; +} + +.odd { background-color: cornflowerblue; +} + +.even { + background-color: lightgreen; +} + +.list-title { + font-size: 20; + text-align: center; } \ No newline at end of file diff --git a/ng-sample/app/examples/list/list-test-async.ts b/ng-sample/app/examples/list/list-test-async.ts index 5631d22dc..b7a23d506 100644 --- a/ng-sample/app/examples/list/list-test-async.ts +++ b/ng-sample/app/examples/list/list-test-async.ts @@ -1,58 +1,117 @@ import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; -import { Observable as RxObservable } from 'rxjs/Observable'; - -export class DataItem { - constructor(public id: number, public name: string) { } -} +import * as Rx from 'rxjs/Observable'; +import { combineLatestStatic } from 'rxjs/operator/combineLatest'; +import { BehaviorSubject } from "rxjs/BehaviorSubject"; +import { DataItem, DataService } from "./data.service" @Component({ selector: 'list-test-async', styleUrls: ['examples/list/list-test-async.css'], + providers: [DataService], + changeDetection: ChangeDetectionStrategy.OnPush, template: ` - - + + + + + + + + + + + + + - `, - changeDetection: ChangeDetectionStrategy.OnPush + ` }) export class ListTestAsync { - public myItems: RxObservable>; + public isUpdating: boolean = false; + constructor(private service: DataService) { + } + + public onItemTap(args) { + console.log("--> ItemTapped: " + args.index); + } - constructor() { - var items = []; - for (var i = 0; i < 3; i++) { - items.push(new DataItem(i, "data item " + i)); + public toggleAsyncUpdates() { + if (this.isUpdating) { + this.service.stopAsyncUpdates(); + } else { + this.service.startAsyncUpdates(); } - - var subscr; - this.myItems = RxObservable.create(subscriber => { - subscr = subscriber; - subscriber.next(items); - return function () { - console.log("Unsubscribe called!!!"); - } - }); - let counter = 2; - let intervalId = setInterval(() => { - counter++; - console.log("Adding " + counter + "-th item"); - items.push(new DataItem(counter, "data item " + counter)); - subscr.next(items); - }, 1000); - - setTimeout(() => { - clearInterval(intervalId); - }, 15000); + this.isUpdating = !this.isUpdating; + } +} + +@Component({ + selector: 'list-test-async-filter', + styleUrls: ['examples/list/list-test-async.css'], + providers: [DataService], + // changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + + + + + + + + + + + + + + + + + ` +}) +export class ListTestFilterAsync { + public isUpdating: boolean = false; + public filteredItems$: Rx.Observable>; + private filter$ = new BehaviorSubject(false); + + constructor(private service: DataService) { + // Create filteredItems$ by combining the service.items$ and filter$ + this.filteredItems$ = combineLatestStatic(this.service.items$, this.filter$, (data, filter) => { + return filter ? data.filter(v => v.id % 2 === 0) : data; + }); } public onItemTap(args) { - console.log("------------------------ ItemTapped: " + args.index); + console.log("--> ItemTapped: " + args.index); } -} + + public toggleAsyncUpdates() { + if (this.isUpdating) { + this.service.stopAsyncUpdates(); + } else { + this.service.startAsyncUpdates(); + } + + this.isUpdating = !this.isUpdating; + } + + public toogleFilter() { + this.filter$.next(!this.filter$.value); + } +} \ No newline at end of file diff --git a/ng-sample/hooks/before-livesync/nativescript-angular-sync .js b/ng-sample/hooks/before-livesync/nativescript-angular-sync .js index 8d279c8c6..5a8510cb2 100644 --- a/ng-sample/hooks/before-livesync/nativescript-angular-sync .js +++ b/ng-sample/hooks/before-livesync/nativescript-angular-sync .js @@ -1 +1 @@ -module.exports = require("nativescript-angular/hooks/before-livesync"); +module.exports = require("nativescript-angular/hooks/before-livesync"); From 25309187d51ca3fc3bd287cf5e9e05fbf026f414 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 9 Jun 2016 14:09:15 +0300 Subject: [PATCH 2/6] list-test updated --- .../app/examples/list/list-test-async.ts | 4 +- ng-sample/app/examples/list/list-test.ts | 42 +++++++++++-------- .../list/{list-test-async.css => styles.css} | 0 3 files changed, 27 insertions(+), 19 deletions(-) rename ng-sample/app/examples/list/{list-test-async.css => styles.css} (100%) diff --git a/ng-sample/app/examples/list/list-test-async.ts b/ng-sample/app/examples/list/list-test-async.ts index b7a23d506..82a5ffb3e 100644 --- a/ng-sample/app/examples/list/list-test-async.ts +++ b/ng-sample/app/examples/list/list-test-async.ts @@ -6,7 +6,7 @@ import { DataItem, DataService } from "./data.service" @Component({ selector: 'list-test-async', - styleUrls: ['examples/list/list-test-async.css'], + styleUrls: ['examples/list/styles.css'], providers: [DataService], changeDetection: ChangeDetectionStrategy.OnPush, template: ` @@ -55,7 +55,7 @@ export class ListTestAsync { @Component({ selector: 'list-test-async-filter', - styleUrls: ['examples/list/list-test-async.css'], + styleUrls: ['examples/list/styles.css'], providers: [DataService], // changeDetection: ChangeDetectionStrategy.OnPush, template: ` diff --git a/ng-sample/app/examples/list/list-test.ts b/ng-sample/app/examples/list/list-test.ts index 1b9e669cc..aaeaca5b0 100644 --- a/ng-sample/app/examples/list/list-test.ts +++ b/ng-sample/app/examples/list/list-test.ts @@ -8,10 +8,10 @@ class DataItem { @Component({ selector: 'item-component', + styleUrls: ['examples/list/styles.css'], template: ` - - + ` }) @@ -24,20 +24,28 @@ export class ItemComponent { @Component({ selector: 'list-test', + styleUrls: ['examples/list/styles.css'], directives: [ItemComponent], template: ` - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -65,17 +73,17 @@ export class ListTest { //this.myItems = new ObservableArray(); this.myItems = []; this.counter = 0; - for (var i = 0; i < 50; i++) { + for (var i = 0; i < 10; i++) { this.myItems.push(new DataItem(i, "data item " + i)); this.counter = i; } } public onItemTap(args) { - console.log("------------------------ ItemTapped: " + args.index); + console.log("--> ItemTapped: " + args.index); } - onButtonTap() { + addItem() { this.counter++; this.myItems.push(new DataItem(this.counter, "data item " + this.counter)); } diff --git a/ng-sample/app/examples/list/list-test-async.css b/ng-sample/app/examples/list/styles.css similarity index 100% rename from ng-sample/app/examples/list/list-test-async.css rename to ng-sample/app/examples/list/styles.css From 2b36449d70d559d1fda6cdf4d271246b5fe0ecd2 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 10 Jun 2016 17:39:08 +0300 Subject: [PATCH 3/6] Fix: Cyclic change detection when list view is present --- .../directives/list-view-comp.ts | 48 ++++++++++--------- ng-sample/app/examples/list/list-test.ts | 32 ++++++++----- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts index 5d35d3df2..0eb4d2d8f 100644 --- a/nativescript-angular/directives/list-view-comp.ts +++ b/nativescript-angular/directives/list-view-comp.ts @@ -1,18 +1,19 @@ import { - Component, - DoCheck, - ElementRef, + Component, + DoCheck, + ElementRef, ViewContainerRef, - TemplateRef, - ContentChild, + TemplateRef, + ContentChild, EmbeddedViewRef, - HostListener, - IterableDiffers, + HostListener, + IterableDiffers, IterableDiffer, ChangeDetectorRef, EventEmitter, ViewChild, Output, + NgZone, ChangeDetectionStrategy} from '@angular/core'; import {isBlank} from '@angular/core/src/facade/lang'; import {isListLikeIterable} from '@angular/core/src/facade/collection'; @@ -55,11 +56,11 @@ export class ListViewComponent { public get nativeElement(): ListView { return this.listView; } - + private listView: ListView; private _items: any; private _differ: IterableDiffer; - + @ViewChild('loader', { read: ViewContainerRef }) loader: ViewContainerRef; @Output() public setupItemView: EventEmitter = new EventEmitter(); @@ -73,17 +74,18 @@ export class ListViewComponent { needDiffer = false; } if (needDiffer && !this._differ && isListLikeIterable(value)) { - this._differ = this._iterableDiffers.find(this._items).create(this._cdr, (index, item) => { return item;}); + this._differ = this._iterableDiffers.find(this._items).create(this._cdr, (index, item) => { return item; }); } this.listView.items = this._items; } private timerId: number; private doCheckDelay = 5; - + constructor(private _elementRef: ElementRef, - private _iterableDiffers: IterableDiffers, - private _cdr: ChangeDetectorRef) { + private _iterableDiffers: IterableDiffers, + private _cdr: ChangeDetectorRef, + private _zone: NgZone) { this.listView = _elementRef.nativeElement; } @@ -126,7 +128,7 @@ export class ListViewComponent { context.even = (index % 2 == 0); context.odd = !context.even; - this.setupItemView.next({view: viewRef, data: data, index: index, context: context}); + this.setupItemView.next({ view: viewRef, data: data, index: index, context: context }); } ngDoCheck() { @@ -134,15 +136,17 @@ export class ListViewComponent { clearTimeout(this.timerId); } - this.timerId = setTimeout(() => { - clearTimeout(this.timerId); - if (this._differ) { - var changes = this._differ.diff(this._items); - if (changes) { - this.listView.refresh(); + this._zone.runOutsideAngular(() => { + this.timerId = setTimeout(() => { + clearTimeout(this.timerId); + if (this._differ) { + var changes = this._differ.diff(this._items); + if (changes) { + this.listView.refresh(); + } } - } - }, this.doCheckDelay); + }, this.doCheckDelay); + }); } } diff --git a/ng-sample/app/examples/list/list-test.ts b/ng-sample/app/examples/list/list-test.ts index aaeaca5b0..5cdb59bf5 100644 --- a/ng-sample/app/examples/list/list-test.ts +++ b/ng-sample/app/examples/list/list-test.ts @@ -1,4 +1,4 @@ -import {Component, Input, WrappedValue, ChangeDetectionStrategy} from '@angular/core'; +import {Component, Input, WrappedValue, ChangeDetectionStrategy, AfterViewChecked, DoCheck} from '@angular/core'; import {Label} from 'ui/label'; import {ObservableArray} from 'data/observable-array'; @@ -8,24 +8,34 @@ class DataItem { @Component({ selector: 'item-component', - styleUrls: ['examples/list/styles.css'], + styleUrls: ['examples/list/styles.css'], + // changeDetection: ChangeDetectionStrategy.OnPush, template: ` - + ` }) -export class ItemComponent { +export class ItemComponent implements AfterViewChecked,DoCheck { @Input() data: DataItem; @Input() odd: boolean; @Input() even: boolean; constructor() { } + + ngDoCheck(){ + // console.log("ItemComponent.ngDoCheck: " + this.data.id); + } + + ngAfterViewChecked(){ + // console.log("ItemComponent.ngAfterViewChecked: " + this.data.id); + } } @Component({ selector: 'list-test', styleUrls: ['examples/list/styles.css'], directives: [ItemComponent], + // changeDetection: ChangeDetectionStrategy.OnPush, template: ` @@ -36,7 +46,6 @@ export class ItemComponent { - @@ -46,11 +55,10 @@ export class ItemComponent { - + - `, - changeDetection: ChangeDetectionStrategy.OnPush + ` // TEMPLATE WITH COMPONENT //