From 1449a9e0fcbd546388f5eb006e9051a7e24944fa Mon Sep 17 00:00:00 2001 From: alexzuza Date: Sat, 4 Feb 2017 22:06:09 +0400 Subject: [PATCH 1/2] Initial commit --- app/app-routing.module.ts | 30 ++ app/app.component.ts | 33 +- app/app.module.ts | 31 +- .../default-change-detection.component.ts | 142 ++++++++ .../manual-change-detection.component.ts | 340 ++++++++++++++++++ app/pages/not-found/not-found.component.ts | 7 + ...-change-detection-observables.component.ts | 334 +++++++++++++++++ .../on-push-change-detection.component.ts | 302 ++++++++++++++++ app/secure/auth-guard.service.ts | 59 +++ app/secure/auth.service.ts | 23 ++ app/services/logger.service.ts | 27 ++ app/services/toggle-class.service.ts | 11 + index.html | 1 + style.css | 283 +++++++++++++++ tsconfig.json | 1 + 15 files changed, 1619 insertions(+), 5 deletions(-) create mode 100644 app/app-routing.module.ts create mode 100644 app/pages/default/default-change-detection.component.ts create mode 100644 app/pages/manual/manual-change-detection.component.ts create mode 100644 app/pages/not-found/not-found.component.ts create mode 100644 app/pages/on-push-observables/on-push-change-detection-observables.component.ts create mode 100644 app/pages/on-push/on-push-change-detection.component.ts create mode 100644 app/secure/auth-guard.service.ts create mode 100644 app/secure/auth.service.ts create mode 100644 app/services/logger.service.ts create mode 100644 app/services/toggle-class.service.ts diff --git a/app/app-routing.module.ts b/app/app-routing.module.ts new file mode 100644 index 0000000..739b378 --- /dev/null +++ b/app/app-routing.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + + +import { NotFoundComponent } from './pages/not-found/not-found.component'; +import { DefaultChangeDetectionComponent } from './pages/default/default-change-detection.component'; +import { ManualChangeDetectionComponent } from './pages/manual/manual-change-detection.component'; +import { OnPushChangeDetectionComponent } from './pages/on-push/on-push-change-detection.component'; +import { OnPushChangeDetectionObservablesComponent } from './pages/on-push-observables/on-push-change-detection-observables.component'; + +import { AuthGuard } from './secure/auth-guard.service'; + +const appRoutes: Routes = [ + { path: 'default-change-detection', component: DefaultChangeDetectionComponent }, + { path: 'on-push-change-detection', component: OnPushChangeDetectionComponent }, + { path: 'on-push-change-detection-observables', component: OnPushChangeDetectionObservablesComponent }, + { path: 'manual-change-detection', component: ManualChangeDetectionComponent }, + { path: '', redirectTo: '/default-change-detection', pathMatch: 'full' }, + { path: '**', component: NotFoundComponent } +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(appRoutes) + ], + exports: [ + RouterModule + ] +}) +export class AppRoutingModule { } diff --git a/app/app.component.ts b/app/app.component.ts index 00fd09b..3b0e758 100644 --- a/app/app.component.ts +++ b/app/app.component.ts @@ -1,9 +1,36 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild, forwardRef } from '@angular/core'; + +import { Subject } from 'rxjs/Subject'; @Component({ selector: 'my-app', template: ` -

Angular 2 Systemjs start

+
+

Investigation ng2 Change Detection

+ +
+ +
+ +
+ + ` }) -export class AppComponent { } \ No newline at end of file +export class AppComponent { + ngOnChange() { + + } + + ngOnInit() { + console.log('App component ngOnInit'); + } + +} \ No newline at end of file diff --git a/app/app.module.ts b/app/app.module.ts index e096758..6e37186 100644 --- a/app/app.module.ts +++ b/app/app.module.ts @@ -3,9 +3,36 @@ import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; + +import { DefaultChangeDetectionComponent, defaultComponents } from './pages/default/default-change-detection.component'; +import { ManualChangeDetectionComponent, manualComponents } from './pages/manual/manual-change-detection.component'; +import { OnPushChangeDetectionComponent, onPushComponents } from './pages/on-push/on-push-change-detection.component'; +import { OnPushChangeDetectionObservablesComponent, observableComponents } from './pages/on-push-observables/on-push-change-detection-observables.component'; + +import { NotFoundComponent } from './pages/not-found/not-found.component'; + @NgModule({ - imports: [ BrowserModule ], - declarations: [ AppComponent ], + imports: [ + BrowserModule, + AppRoutingModule + ], + declarations: [ + AppComponent, + DefaultChangeDetectionComponent, + defaultComponents, + + ManualChangeDetectionComponent, + manualComponents, + + OnPushChangeDetectionComponent, + onPushComponents, + + OnPushChangeDetectionObservablesComponent, + observableComponents, + + NotFoundComponent + ], bootstrap: [ AppComponent ] }) export class AppModule { } \ No newline at end of file diff --git a/app/pages/default/default-change-detection.component.ts b/app/pages/default/default-change-detection.component.ts new file mode 100644 index 0000000..408daa1 --- /dev/null +++ b/app/pages/default/default-change-detection.component.ts @@ -0,0 +1,142 @@ +import { Component, ElementRef, NgZone, ViewChild } from '@angular/core'; + +import { Subject } from 'rxjs/Subject'; + +import { toggleClass } from 'app/services/toggle-class.service'; + +@Component({ + selector: 'default-change-detection-component', + template: ` +

Default Change Detection

+

This demo shows how change detection is performed for each component after every VM turn.
All components should light + up when:

+ +
    +
  1. The app is bootstrapped (reload browser to double-check)
  2. +
  3. Click "Trigger CD", performs CD everywhere
  4. +
+ +
+ +
+
+ +
+
+ `, + providers: [] +}) +export class DefaultChangeDetectionComponent {} + + +class ToggleClass { + constructor(private zone: NgZone, private el: ElementRef) { } + + ngAfterViewChecked() { + toggleClass(this.el, this.zone); + } +} + +@Component({ + selector: 'default1', + template: ` + Comp1 + + ` +}) +export class Default1 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + +@Component({ + selector: 'default2', + template: ` + Comp2 + + ` +}) +export class Default2 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'default3', + template: ` + Comp3 + + ` +}) +export class Default3 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'default4', + template: 'Comp4' +}) +export class Default4 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'default5', + template: 'Comp5' +}) +export class Default5 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'default6', + template: 'Comp6' +}) +export class Default6 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'default7', + template: 'Comp7' +}) +export class Default7 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + +export const defaultComponents = [ + Default1, + Default2, + Default3, + Default4, + Default5, + Default6, + Default7 +]; \ No newline at end of file diff --git a/app/pages/manual/manual-change-detection.component.ts b/app/pages/manual/manual-change-detection.component.ts new file mode 100644 index 0000000..d4ca01f --- /dev/null +++ b/app/pages/manual/manual-change-detection.component.ts @@ -0,0 +1,340 @@ +import { Component, ChangeDetectorRef, forwardRef, Inject, ChangeDetectionStrategy, Input, NgZone, ElementRef } from '@angular/core'; +import { Subject } from 'rxjs/Rx'; + +import { toggleClass } from 'app/services/toggle-class.service'; + +@Component({ + selector: 'manual-cd', + template: ` +

Manual Change Detection

+

This demo shows how a change detector is detached from the change detector tree + and performes change detection manually.

+ + +
    +
  1. The app is bootstrapped (reload browser to double-check)
  2. +
  3. Click "Trigger CD", skips Comp2 and Comp6 subtrees
  4. +
  5. Attach cdRef, performs CD everywhere (onPush component is getting input changes)
  6. +
  7. Click "Trigger CD", skips Comp2 subtree
  8. +
+ +
+ + +
+
+ +
+
+ ` +}) +export class ManualChangeDetectionComponent { + attachOrDetachNotifier:Subject = new Subject(); + + attachOrDetachChangeDetector(attach) { + this.attachOrDetachNotifier.next(attach); + } +} + +class ToggleClass { + constructor(protected zone: NgZone, protected el: ElementRef) { } + + ngAfterViewChecked() { + toggleClass(this.el, this.zone); + } +} + +@Component({ + selector: 'manual1', + template: ` + Comp1 + + ` +}) +export class Manual1 extends ToggleClass { + + @Input() notifier:Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual2', + template: ` + Comp2 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Manual2 extends ToggleClass { + + @Input() notifier:Subject; + + constructor(zone: NgZone, el: ElementRef, private cd: ChangeDetectorRef) { + super(zone, el); + } + + ngOnInit() { + this.cd.detach(); + + this.notifier.subscribe((attach) => { + if (attach) { + this.el.nativeElement.querySelector('a').classList.remove('detached'); + this.cd.reattach(); + } else { + this.el.nativeElement.querySelector('a').classList.add('detached'); + this.cd.detach(); + } + }); + } +} + +@Component({ + selector: 'manual3', + template: ` + Comp3 +
    +
  • +
  • +
+ ` +}) +export class Manual3 extends ToggleClass { + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + +@Component({ + selector: 'manual4', + template: ` + Comp4 +
    +
  • +
  • +
+ ` +}) +export class Manual4 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } + + triggerChangeDetection () { } +} + +@Component({ + selector: 'manual5', + template: ` + Comp5 +
    +
  • +
  • +
+ ` +}) +export class Manual5 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual6', + template: ` + Comp6 +
    +
  • +
  • +
+ ` +}) +export class Manual6 extends ToggleClass { + + constructor( + zone: NgZone, el: + ElementRef, private cd: ChangeDetectorRef, + @Inject(forwardRef(() => ManualChangeDetectionComponent)) private parentComp: ManualChangeDetectionComponent) { + super(zone, el); + } + + ngOnInit() { + this.cd.detach(); + + this.parentComp.attachOrDetachNotifier.subscribe((attach) => { + if (attach) { + this.el.nativeElement.querySelector('a').classList.remove('detached'); + this.cd.reattach(); + } else { + this.el.nativeElement.querySelector('a').classList.add('detached'); + this.cd.detach(); + } + }); + } +} + +@Component({ + selector: 'manual7', + template: ` + Comp7 +
    +
  • +
  • +
+ ` +}) +export class Manual7 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + + +@Component({ + selector: 'manual8', + template: ` + Comp8 +
    +
  • +
+ ` +}) +export class Manual8 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + +@Component({ + selector: 'manual9', + template: ` + Comp9 +
    +
  • +
+ ` +}) +export class Manual9 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual10', + template: `Comp10` +}) +export class Manual10 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual11', + template: `Comp11` +}) +export class Manual11 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual12', + template: `Comp12` +}) +export class Manual12 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual13', + template: `Comp13` +}) +export class Manual13 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual14', + template: `Comp14` +}) +export class Manual14 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual15', + template: `Comp15` +}) +export class Manual15 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual16', + template: `Comp16` +}) +export class Manual16 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'manual17', + template: `Comp17` +}) +export class Manual17 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +export const manualComponents = [ + Manual1, + Manual2, + Manual3, + Manual4, + Manual5, + Manual6, + Manual7, + Manual8, + Manual9, + Manual10, + Manual11, + Manual12, + Manual13, + Manual14, + Manual15, + Manual16, + Manual17 +]; \ No newline at end of file diff --git a/app/pages/not-found/not-found.component.ts b/app/pages/not-found/not-found.component.ts new file mode 100644 index 0000000..77dc7ab --- /dev/null +++ b/app/pages/not-found/not-found.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'not-found', + template: 'Sorry, page not found' +}) +export class NotFoundComponent { } \ No newline at end of file diff --git a/app/pages/on-push-observables/on-push-change-detection-observables.component.ts b/app/pages/on-push-observables/on-push-change-detection-observables.component.ts new file mode 100644 index 0000000..b297545 --- /dev/null +++ b/app/pages/on-push-observables/on-push-change-detection-observables.component.ts @@ -0,0 +1,334 @@ +import { Component, ChangeDetectionStrategy, ChangeDetectorRef, Input, NgZone, ElementRef } from '@angular/core'; +import { Subject } from 'rxjs/Rx'; + +import { toggleClass } from 'app/services/toggle-class.service'; + +@Component({ + selector: 'on-push-cd-observables', + template: ` +

OnPush Change Detection (Observables)

+

This demo shows how Angular performs change detection only for a path when Observables are used.

+ +
    +
  1. Bootstrapped app, performs CD everywhere
  2. +
  3. Click "Trigger CD" button, skips "Comp1" subtree
  4. +
  5. Click "Push to Comp17", performs CD only on path from root to Comp17
  6. +
  7. Click "Comp7", skips subtrees of "Comp6", "Comp5", "Comp8" and performs CD on path from root to Comp17
  8. +
+ +
+ + +
+
+
    +
  • + +
  • +
+
+
+ ` +}) +export class OnPushChangeDetectionObservablesComponent { + model: Subject = new Subject(); + + emitAndTriggerChangeDetection() { + this.model.next(null); + } +} + +class ToggleClass { + constructor(protected zone: NgZone, protected el: ElementRef) { } + + ngAfterViewChecked() { + toggleClass(this.el, this.zone); + } +} + +@Component({ + selector: 'observable1', + template: ` + Comp1 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable1 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable2', + template: ` + Comp2 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable2 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + + +@Component({ + selector: 'observable3', + template: ` + Comp3 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable3 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable4', + template: ` + Comp4 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable4 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable5', + template: ` + Comp5 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable5 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable6', + template: ` + Comp6 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable6 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable7', + host: { '(click)':'emitAndTriggerChangeDetection()' }, + template: ` + Comp7 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable7 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } + + emitAndTriggerChangeDetection() { + this.model.next(null); + } +} + +@Component({ + selector: 'observable8', + template: ` + Comp8 +
    +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable8 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable9', + template: ` + Comp9 + +
    +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable9 extends ToggleClass { + @Input() model: Subject; + + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable10', + template: `Comp10`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable10 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable11', + template: `Comp11`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable11 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable12', + template: `Comp12`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable12 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable13', + template: `Comp13`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable13 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable14', + template: `Comp14`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable14 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable15', + template: `Comp15`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable15 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable16', + template: `Comp16`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable16 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'observable17', + template: `Cmp17`, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class Observable17 extends ToggleClass { + @Input() model:Subject; + + constructor(zone: NgZone, el: ElementRef, private cd: ChangeDetectorRef) { + super(zone, el); + } + + ngOnInit() { + this.model.subscribe(() => { + this.cd.markForCheck(); + toggleClass(this.el, this.zone, 'checked-observable'); + }) + } +} + +export const observableComponents = [ + Observable1, + Observable2, + Observable3, + Observable4, + Observable5, + Observable6, + Observable7, + Observable8, + Observable9, + Observable10, + Observable11, + Observable12, + Observable13, + Observable14, + Observable15, + Observable16, + Observable17 +]; \ No newline at end of file diff --git a/app/pages/on-push/on-push-change-detection.component.ts b/app/pages/on-push/on-push-change-detection.component.ts new file mode 100644 index 0000000..06cd4eb --- /dev/null +++ b/app/pages/on-push/on-push-change-detection.component.ts @@ -0,0 +1,302 @@ +import { Component, NgZone, ChangeDetectorRef, ChangeDetectionStrategy, ElementRef, OnInit } from '@angular/core'; +import { Subject } from 'rxjs/Rx'; + +import { toggleClass } from 'app/services/toggle-class.service'; + +@Component({ + selector: 'on-push-cd', + template: ` +

OnPush Change Detection

+

This demo shows how change detection skips a component's subtree when + it's set to OnPush and non of its input property change or an + event is fired inside that component.

+ +
    +
  1. Bootstrapped app, performs CD everywhere (reload to check)
  2. +
  3. Click "Trigger CD" button, skips "Comp2" subtree
  4. +
  5. Click "Comp12", skips "Comp2" subtree
  6. +
  7. Click "Comp10", skips "Comp8" subtree
  8. +
  9. Click "Comp16", performs CD everywhere
  10. +
+ +
+ +
+
+
    +
  • + +
  • +
+
+
+ ` +}) +export class OnPushChangeDetectionComponent { } + +class ToggleClass { + constructor(protected zone: NgZone, protected el: ElementRef) { } + + ngAfterViewChecked() { + toggleClass(this.el, this.zone); + } +} + + +@Component({ + selector: 'onpush1', + template: ` + Comp1 +
    +
  • +
  • +
+ ` +}) +export class OnPush1 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush2', + template: ` + Comp2 +
    +
  • +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OnPush2 { + + counter:number = 0; + + constructor(private zone: NgZone, private el: ElementRef, private cd: ChangeDetectorRef) {} + + ngAfterViewChecked() { + toggleClass(this.el, this.zone); + } +} + +@Component({ + selector: 'onpush3', + template: ` + Comp3 +
    +
  • +
  • +
+ ` +}) +export class OnPush3 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush4', + template: ` + Comp4 +
    +
  • +
  • +
+ ` +}) +export class OnPush4 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush5', + template: ` + Comp4 +
    +
  • +
  • +
+ ` +}) +export class OnPush5 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush6', + template: ` + Comp6 +
    +
  • +
  • +
+ ` +}) +export class OnPush6 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush7', + template: ` + Comp7 +
    +
  • +
  • +
+ ` +}) +export class OnPush7 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush8', + template: ` + Comp8 +
    +
  • +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class OnPush8 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush9', + template: ` + Comp9 +
    +
  • +
+ ` +}) +export class OnPush9 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush10', + host: { '(click)': 'triggerChangeDetection()' }, + template: `Comp10` +}) +export class OnPush10 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } + + triggerChangeDetection () { } +} + +@Component({ + selector: 'onpush11', + template: `Comp11` +}) +export class OnPush11 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush12', + host: { '(click)': 'triggerChangeDetection()' }, + template: `Comp12` +}) +export class OnPush12 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } + + triggerChangeDetection () { } +} + +@Component({ + selector: 'onpush13', + template: `Comp13` +}) +export class OnPush13 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush14', + template: `Comp14` +}) +export class OnPush14 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush15', + template: `Comp15` +}) +export class OnPush15 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +@Component({ + selector: 'onpush16', + host: { '(click)': 'triggerChangeDetection()' }, + template: `Comp16` +}) +export class OnPush16 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } + + triggerChangeDetection () { } +} + +@Component({ + selector: 'onpush17', + template: `Comp17` +}) +export class OnPush17 extends ToggleClass { + constructor(zone: NgZone, el: ElementRef) { + super(zone, el); + } +} + +export const onPushComponents = [ + OnPush1, + OnPush2, + OnPush3, + OnPush4, + OnPush5, + OnPush6, + OnPush7, + OnPush8, + OnPush9, + OnPush10, + OnPush11, + OnPush12, + OnPush13, + OnPush14, + OnPush15, + OnPush16, + OnPush17 +]; \ No newline at end of file diff --git a/app/secure/auth-guard.service.ts b/app/secure/auth-guard.service.ts new file mode 100644 index 0000000..887fc59 --- /dev/null +++ b/app/secure/auth-guard.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild, + NavigationExtras, + CanLoad, Route +} from '@angular/router'; +import { AuthService } from './auth.service'; + +@Injectable() +export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { + constructor(private authService: AuthService, private router: Router) {} + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + let url: string = state.url; + + return this.checkLogin(url); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + return this.canActivate(route, state); + } + + canLoad(route: Route): boolean { + let url = `/${route.path}`; + + return this.checkLogin(url); + } + + checkLogin(url: string): boolean { + if (this.authService.isLoggedIn) { return true; } + + // Store the attempted URL for redirecting + this.authService.redirectUrl = url; + + // Create a dummy session id + let sessionId = 123456789; + + // Set our navigation extras object + // that contains our global query params and fragment + let navigationExtras: NavigationExtras = { + queryParams: { 'session_id': sessionId }, + fragment: 'anchor' + }; + + // Navigate to the login page with extras + this.router.navigate(['/login'], navigationExtras); + return false; + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/secure/auth.service.ts b/app/secure/auth.service.ts new file mode 100644 index 0000000..b55d7af --- /dev/null +++ b/app/secure/auth.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; + +import { Observable } from 'rxjs/Observable'; + +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/delay'; + +@Injectable() +export class AuthService { + isLoggedIn: boolean = false; + + // store the URL so we can redirect after logging in + redirectUrl: string; + + login(): Observable { + return Observable.of(true).delay(1000).do(val => this.isLoggedIn = true); + } + + logout(): void { + this.isLoggedIn = false; + } +} diff --git a/app/services/logger.service.ts b/app/services/logger.service.ts new file mode 100644 index 0000000..c885b50 --- /dev/null +++ b/app/services/logger.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class LogerService { + logs: string[] = []; + prevMsg = ''; + prevMsgCount = 1; + + log(msg: string) { + if (msg === this.prevMsg) { + // Repeat message; update last log entry with count. + this.logs[this.logs.length - 1] = msg + ` (${this.prevMsgCount += 1}x)`; + } else { + // New message; log it. + this.prevMsg = msg; + this.prevMsgCount = 1; + this.logs.push(msg); + } + } + + clear() { this.logs.length = 0; } + + // schedules a view refresh to ensure display catches up + tick() { this.tick_then(() => { }); } + + tick_then(fn: () => any) { setTimeout(fn, 0); } +} \ No newline at end of file diff --git a/app/services/toggle-class.service.ts b/app/services/toggle-class.service.ts new file mode 100644 index 0000000..eca7d3d --- /dev/null +++ b/app/services/toggle-class.service.ts @@ -0,0 +1,11 @@ + +export function toggleClass(el, zone, className = 'checked') { + let a = el.nativeElement.querySelector('a'); + a.classList.add(className); + zone.runOutsideAngular(() => { + setTimeout(() => { + a.classList.remove(className); + }, 400); + }) +} + diff --git a/index.html b/index.html index 6fa427d..e0bd1cd 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ Angular 2 QuickStart + diff --git a/style.css b/style.css index e69de29..e82f1b3 100644 --- a/style.css +++ b/style.css @@ -0,0 +1,283 @@ +html, +body, +my-app { + height: 100%; +} + + +body { + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + color: #555; + -webkit-font-smoothing:antialiased; +} + +a { + color: #0f97f9; +} + +p { + line-height: 24px; +} +p, +.list{ + margin-top: 1em; +} + +.list li { + margin-left: 1em; + margin-top: 0.5em; +} + +button { + padding: 1em; + font-size: 16px; + border-radius: 0.3em; + border: 0; + background: #607d8b; + color: #fff; + font-weight: bold; + margin-top: 2em; + outline: none; +} + +button:hover { cursor: pointer; } + + +body { + margin: 0; +} + +my-app { + display: block; + display: flex; + flex-flow: column; +} + +.top { + background: #369; + color: #fff; +} + +.body { + flex: 1 1 auto; + overflow-y: auto; + padding: 20px; +} + +.bottom { + background: #444; + color: #fff; + padding: 20px; +} + +.site-title { + padding: 20px; +} + +.nav { + display: flex; + padding: 10px 20px; + background: #e2e2e2; +} + +.nav-item { + padding: 10px; + color: #212121; + margin-right: 10px; +} + +.nav-item.active { + background: #fff; +} + + +/* + Tree styles +*/ + +/************************************************************************** + * TREE STYLES + *************************************************************************/ + +.tree { margin-top: 1em; } +.tree ul { + padding-top: 20px; position: relative; + + transition: all 0.5s; + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; +} + +.tree li { + float: left; text-align: center; + list-style-type: none; + position: relative; + padding: 20px 5px 0 5px; + + transition: all 0.5s; + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; +} + +/*We will use ::before and ::after to draw the connectors*/ + +.tree li::before, .tree li::after{ + content: ''; + position: absolute; top: 0; right: 50%; + border-top: 1px solid #ccc; + width: 50%; height: 20px; +} +.tree li::after{ + right: auto; left: 50%; + border-left: 1px solid #ccc; +} + +/*We need to remove left-right connectors from elements without +any siblings*/ +.tree li:only-child::after, .tree li:only-child::before { + display: none; +} + +/*Remove space from the top of single children*/ +.tree li:only-child{ padding-top: 0;} + +/*Remove left connector from first child and +right connector from last child*/ +.tree li:first-child::before, .tree li:last-child::after{ + border: 0 none; +} +/*Adding back the vertical connector to the last nodes*/ +.tree li:last-child::before{ + border-right: 1px solid #ccc; + border-radius: 0 5px 0 0; + -webkit-border-radius: 0 5px 0 0; + -moz-border-radius: 0 5px 0 0; +} +.tree li:first-child::after{ + border-radius: 5px 0 0 0; + -webkit-border-radius: 5px 0 0 0; + -moz-border-radius: 5px 0 0 0; +} + +/*Time to add downward connectors from parents*/ +.tree ul ul::before{ + content: ''; + position: absolute; top: 0; left: 50%; + border-left: 1px solid #ccc; + width: 0; height: 20px; +} + +.tree li a, +.tree li .component{ + background: #c8e4f8; + color: #000; + border: 1px solid #94a0b4; + padding: 2em 2.5em; + text-decoration: none; + font-family: arial, verdana, tahoma; + font-size: 16px; + font-weight: bold; + display: inline-block; + + border-radius: 5px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + + transition: all 0.5s; + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; +} + +/*Time for some hover effects*/ +/*We will apply the hover effect the the lineage of the element also*/ +.tree li a:hover, .tree li a:hover+ul li a { +/* .tree li .component:hover, .tree li .component:hover+ul li .component { */ + background: #94a0b4; color: #000; border: 1px solid #555; +} +/*Connector styles on hover*/ +.tree li a:hover+ul li::after, +.tree li a:hover+ul li::before, +.tree li a:hover+ul::before, +.tree li a:hover+ul ul::before, +.tree li .component:hover+ul li::after, +.tree li .component:hover+ul li::before, +.tree li .component:hover+ul::before, +.tree li .component:hover+ul ul::before{ + border-color: #94a0b4; +} + +.tree li a.checked { + background: #bada55; +} + +.tree li a.on-push { + background: #CCCED3; + position: relative; +} + +.tree li a.detached { + background: #CCCED3; + position: relative; +} +.tree li a.detached.checked, +.tree li a.on-push.checked { + background: #bada55; +} + +.tree li a.on-push.subscriber { + background: #85A6E8; +} + + +.tree li a.on-push.checked-observable { + background: #F4C812; +} + +.tree li a.detached:after { + position: absolute; + content: 'Detached'; + right: -3em; + background: white; + border: 1px solid #77819E; + color: #333; + top: 0.4em; + margin-right: 15px; + line-height: 24px; + padding: 0em 0.2em; +} +.tree li a.on-push:before { + position: absolute; + content: 'OnPush'; + left: -1em; + background: white; + border: 1px solid #77819E; + color: #333; + top: 0.4em; + line-height: 24px; + padding: 0em 0.2em; +} + +.tree li a.click-me { + position: relative; + -webkit-animation: pusate 1s infinite alternate; + -moz-animation: pusate 1s infinite alternate; + -animation: pusate 1s infinite alternate; + text-shadow: 0 0 8px #ccc; +} +.tree li a.click-me:hover { + cursor: pointer; +} + + +@-webkit-keyframes pusate { + from { box-shadow: 0 0 10px #fff; } + to { box-shadow: 0 0 20px #c00; } +} +@-moz-keyframes pusate { + from { box-shadow: 0 0 10px #fff; } + to { box-shadow: 0 0 20px #c00; } +} +@keyframes pusate { + from { box-shadow: 0 0 10px #fff; } + to { box-shadow: 0 0 20px #c00; } +} diff --git a/tsconfig.json b/tsconfig.json index e51a470..c370444 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": "", "target": "es5", "module": "commonjs", "moduleResolution": "node", From 377ac995c5026300c8d5636aa6f106a3136b23b3 Mon Sep 17 00:00:00 2001 From: alexzuza Date: Sun, 5 Feb 2017 19:55:04 +0400 Subject: [PATCH 2/2] Add pre loader --- index.html | 4 +++- style.css | 24 +++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index e0bd1cd..5879e41 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,9 @@ - Loading... + + + \ No newline at end of file diff --git a/style.css b/style.css index e82f1b3..fb7a49f 100644 --- a/style.css +++ b/style.css @@ -4,11 +4,10 @@ my-app { height: 100%; } - body { font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; color: #555; - -webkit-font-smoothing:antialiased; + margin: 0; } a { @@ -42,17 +41,28 @@ button { button:hover { cursor: pointer; } - -body { - margin: 0; -} - my-app { display: block; display: flex; flex-flow: column; } +loader { + display: block; + height: 100%; + position: relative; + background: #333; +} + +loader:after { + content: 'Loading app. Please waiting...'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #fff; +} + .top { background: #369; color: #fff;