From 70d4e19cc4b8eed54c892b73188d1f014a936ed7 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 1 Nov 2018 11:58:43 -0400 Subject: [PATCH] ang6 notes Signed-off-by: Bill Erickson --- ang6-toturial.adoc | 333 ++-- ang6-toturial.html | 4452 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 4696 insertions(+), 89 deletions(-) create mode 100644 ang6-toturial.html diff --git a/ang6-toturial.adoc b/ang6-toturial.adoc index fee02f5c7..e074206cf 100644 --- a/ang6-toturial.adoc +++ b/ang6-toturial.adoc @@ -1,53 +1,23 @@ -= Angular6 / Evergreen App Tutorial += Angular6 / Evergreen Cheet Sheet :author: Bill Erickson, Software Development Engineer, King County Library System :email: berickxx@gmail.com :backend: slidy :max-width: 45em :deckjs_theme: web-2.0 -== Modules == - -Modules serve 2 main functions: - -* Importing and exporting dependencies. -* Defining routes and entry points for pages - -== Modules == - -[source,js] ------------------------------------------------------------------------- -@NgModule({ - declarations: [ - // List of components declared by this module. Every component used - // must be declared exactly once throughout the entire application - // including lazy-loaded modules! - ], - imports: [ - // List of imported modules. These modules may be needed by the - // current module or imported so they can be re-exported (below). - ], - exports: [ - // List of components and modules made implicitly - // available to any module that imports this module. - ], - providers: [ - // List of services available to this module and any - // module that imports this module. - // NOTE: @Injectable({providedIn: 'root'}) are globally provided - // and share a single execution context. - ] -}) ------------------------------------------------------------------------- - -== Data Flow == +== Components == == Data Flow: Passing Static Strings To Components == +* Invoke component + [source,html] ---------------------------------------------------------------------- - + ---------------------------------------------------------------------- +* Component code + [source,js] ---------------------------------------------------------------------- @Input() someAttr: string; @@ -55,50 +25,43 @@ Modules serve 2 main functions: == Data Flow: Passing Dynamic Values To Components == +* Invoke component + [source,html] ---------------------------------------------------------------------- ---------------------------------------------------------------------- -* Read Once +* Component Code: Read Once [source,js] ---------------------------------------------------------------------- @Input() someAttr: string; ---------------------------------------------------------------------- -* Respond to Value Changes +* Component Code: Respond to Value Changes +* Handle delayed input data [source,js] ---------------------------------------------------------------------- _someAttr: string; @Input() set someAttr(a: string) { + console.debug("Caller changed value for someAttr: ", a); this._someAttr = string; } ---------------------------------------------------------------------- -== Data Flow: Passing Dynamic Values To/From Components == - -This is most like AngularJS 'ng-model="someVar"'; - -[source,html] ----------------------------------------------------------------------- - ----------------------------------------------------------------------- - -NOTE: custom components typically don't implement [(ngModel)] but -instead use a combination of input and output channels. - - == Data Flow: Responding to Component Events == +* Invoke Component + [source,html] ---------------------------------------------------------------------- ---------------------------------------------------------------------- -* Component +* Component Code [source,js] ---------------------------------------------------------------------- @@ -106,7 +69,7 @@ instead use a combination of input and output channels. // ... this.someAttr = new EventEmitter(); // ... -this.someAttr.emit('Hello World'); +this.someAttr.emit('Hello World'); // "Hello World" == $event ---------------------------------------------------------------------- * Caller can also listen for events in the code @@ -116,13 +79,47 @@ this.someAttr.emit('Hello World'); myComponentRef.someAttr.subscribe(s => this.myVar = s); ---------------------------------------------------------------------- -== Templates / Syntax == +== Data Flow: Passing Dynamic Values To/From Components == + +This is most like AngularJS 'ng-model="someVar"'; + +* Invoke Component [source,html] ---------------------------------------------------------------------- + +---------------------------------------------------------------------- -*ngFor -*ngIf +NOTE: custom components typically don't implement [(ngModel)] but +instead use a combination of input and output channels. + + +== Data Flow: Direct Communication + +* Calling Code + +[source,html] +---------------------------------------------------------------------- + +---------------------------------------------------------------------- + +[source,js] +---------------------------------------------------------------------- +@ViewChild('myComponent') myCompRef: MyComponent; +// ... +this.myCompRef.someAttr = this.whatever; +this.myCompRef.someFunction(); +---------------------------------------------------------------------- + +== Templates / Syntax == + +[source,html] +---------------------------------------------------------------------- +
+
+ I like {{blue}} +
+
@@ -135,68 +132,226 @@ myComponentRef.someAttr.subscribe(s => this.myVar = s); == Template Variables == +* Invoking an AddComponent + [source,html] ---------------------------------------------------------------------- - + + + + Welcome to Mattress City of {{city}}, {{state}}! + - - + + + +---------------------------------------------------------------------- + +* AddComponent Template + ---------------------------------------------------------------------- + + +---------------------------------------------------------------------- + +* AddComponent Code + +[source,js] +---------------------------------------------------------------------- +@Input() adTemplate: TemplateRef; +---------------------------------------------------------------------- + +== Lifecycle hooks + +https://angular.io/guide/lifecycle-hooks + +* ngOnInit() {} used frequently + +== Canned event bindings, neat! == + +* https://angular.io/guide/user-input [source,js] ---------------------------------------------------------------------- -componentRef = @ViewChild('myComponent'); -componentRef.doSomething(); + ---------------------------------------------------------------------- -// in the code -myComp.foo.subscribe(b => myBool=b); +== Modules == + +Modules serve 2 main functions: + +* Importing and exporting dependencies. +* Defining routes and entry points for pages + +== Modules == + +[source,js] +------------------------------------------------------------------------ +@NgModule({ + declarations: [ + /* + List of components declared by this module. Every component used + must be declared exactly once throughout the entire application + including lazy-loaded modules! + */ + ], + imports: [ + /* + List of imported modules. These modules may be needed by the + current module or imported so they can be re-exported (below). + */ + ], + exports: [ + /* + List of components and modules made implicitly + available to any module that imports this module. + */ + ], + providers: [ + /* + List of services available to this module and any + module that imports this module. + NOTE: @Injectable({providedIn: 'root'}) are globally provided + and share a single execution context. + */ + ] +}) +------------------------------------------------------------------------ -== keyboard actions, neat == -(keyup.enter)="submit()" -== observables == +== Routing: Parameters == -value of .pipe(tap(...)) +* Routing code -== Routing == +[source,js] +---------------------------------------------------------------------- +const routes: Routes = [ + // ... + { path: 'record/:id', + component: RecordComponent + }, { + path: 'record/:id/:tab', + component: RecordComponent + } +]; +---------------------------------------------------------------------- -The route API is complex, but expressive. +== Routing: Parameters == -REVISIT WITH SIMPLER EAMPLE TODO -Simple example: +* Component code -Execution context /eg2/staff/admin/server/config +[source,js] +---------------------------------------------------------------------- constructor(private route: ActivatedRoute) {} +ngOnInit() { + this.route.paramMap.subscribe((params: ParamMap) => { + this.recordId = +params.get('id'); + this.recordTab = params.get('tab') || 'copy_table'; + this.loadRecord(); + }); +} +---------------------------------------------------------------------- -this.route.snapshot.parent.url[0].path === 'config' +== Routing: Lazy Loading == -== lifecycle hooks +[source,js] +---------------------------------------------------------------------- +const routes: Routes = [{ + path: '', + component: StaffComponent, + resolve: {staffResolver : StaffResolver}, + children: [{ + path: '', + redirectTo: 'splash', + pathMatch: 'full', + }, { + path: 'login', + component: StaffLoginComponent + }, { + path: 'catalog', + loadChildren : '@eg/staff/catalog/catalog.module#CatalogModule' + }, // ... + }] +}]; +---------------------------------------------------------------------- -ngOnInit used frequently -https://angular.io/guide/lifecycle-hooks +== Misc. == +== Observables == -== GOTCHAS -=== Error handling +* http://reactivex.io/rxjs/manual/overview.html#observable -* fatal errors (route-preventing) returns to base app page. +[source,js] +---------------------------------------------------------------------- +import {Observable} from 'rxjs/Observable'; +import {map} from 'rxjs/operators/map'; + +getThings(): Observable { + return this.net.request(service, method, arg) + .pipe( + map(oneValue => { + oneValue.bar = 'baz'; + return oneValue; + }) + ); +} +---------------------------------------------------------------------- -=== Methods vs. Functions +[source,js] +---------------------------------------------------------------------- +thing.getThings().subscribe(oneValue => console.log(oneValue.bar)); +---------------------------------------------------------------------- -(click)="doStuff()" => this.doStuff() => this == component instance -[handleClick]="doStuff" => doStuff() => this == the HTML element +== Gotchas: Error handling -== compiling +* Fatal errors that prevent routing return to base route. -* diffs between dev and prod (jit/aot) +== Compiling + +* JIT Compiler +** Angular compiler is shipped to the browser. +** Angular templates are compiled at run time. + +* AOT Compiler (--prod) +** Templates are pre-compiled to JS. +** About 1/3 of download size. + +* ng-lint +** Can also warn on imports + +* --watch +** Kill and restart after any major changes (moving modules around, + adding new components, etc.) + +== My Dev Setup + +* Symlink the build destination directory + +[source,sh] +---------------------------------------------------------------------- +ln -s /home/berick/code/Evergreen/Open-ILS/web/eg2/en-US \ + /openils/var/web/eg2/en-US +---------------------------------------------------------------------- + +* Keep a terminal tab open with build running + +[source,sh] +---------------------------------------------------------------------- +ng build --watch +---------------------------------------------------------------------- + +* Occasionally / after major changes + +[source,sh] +---------------------------------------------------------------------- +ng-lint +ng build --prod # then test +---------------------------------------------------------------------- -compile regularly with --prod to verify -and ng lint +== Big Questions -in --watch mode kill and restart after any major changes -(moving modules around, adding new components, etc.) +* Configurable Print Templates diff --git a/ang6-toturial.html b/ang6-toturial.html new file mode 100644 index 000000000..81d3a383f --- /dev/null +++ b/ang6-toturial.html @@ -0,0 +1,4452 @@ + + + + +Angular6 / Evergreen Cheet Sheet + + + + + + + + +
+

Components

+
+
+
+
+

Data Flow: Passing Static Strings To Components

+
+
    +
  • + +Invoke component + +
  • +
+
+
+
<my-component someAttr="Hello World"></my-component>
+
    +
  • + +Component code + +
  • +
+
+
+
@Input() someAttr: string;
+
+
+
+

Data Flow: Passing Dynamic Values To Components

+
+
    +
  • + +Invoke component + +
  • +
+
+
+
<my-component [someAttr]="someVar"></my-component>
+
    +
  • + +Component Code: Read Once + +
  • +
+
+
+
@Input() someAttr: string;
+
    +
  • + +Component Code: Respond to Value Changes + +
  • +
  • + +Handle delayed input data + +
  • +
+
+
+
_someAttr: string;
+@Input() set someAttr(a: string) {
+    console.debug("Caller changed value for someAttr: ", a);
+    this._someAttr = string;
+}
+
+
+
+

Data Flow: Responding to Component Events

+
+
    +
  • + +Invoke Component + +
  • +
+
+
+
<my-component (someAttr)="someHandler($event, ...)"></my-component>
+<my-component (someAttr)="myVar=$event"></my-component>
+
    +
  • + +Component Code + +
  • +
+
+
+
@Output someAttr: EventEmitter<string>;
+// ...
+this.someAttr = new EventEmitter<string>();
+// ...
+this.someAttr.emit('Hello World'); // "Hello World" == $event
+
    +
  • + +Caller can also listen for events in the code + +
  • +
+
+
+
myComponentRef.someAttr.subscribe(s => this.myVar = s);
+
+
+
+

Data Flow: Passing Dynamic Values To/From Components

+
+

This is most like AngularJS ng-model="someVar";

+
    +
  • + +Invoke Component + +
  • +
+
+
+
<my-component [(ngModel)]="someVar"></my-component>
+
+ + + +
+
Note
+
custom components typically don’t implement [(ngModel)] but +instead use a combination of input and output channels.
+
+
+
+
+

Data Flow: Direct Communication

+
+
    +
  • + +Calling Code + +
  • +
+
+
+
<my-component #myComponent></my-component>
+
+
+
@ViewChild('myComponent') myCompRef: MyComponent;
+// ...
+this.myCompRef.someAttr = this.whatever;
+this.myCompRef.someFunction();
+
+
+
+

Templates / Syntax

+
+
+
+
<div *ngFor="let color of colors">
+  <div *ngIf="color == 'blue'">
+    <span i18n>I like {{blue}}</span>
+  </div>
+</div>
+
+<ng-template>
+    <!-- Pre-compiled template executed on demand -->
+</ng-template>
+
+<ng-container>
+    <!-- No-Op container, good for combingin with ngFor/ngIf -->
+</ng-container>
+
+
+
+

Template Variables

+
+
    +
  • + +Invoking an AddComponent + +
  • +
+
+
+
<!-- content I want to render -->
+<ng-template #myTemplate let-city="adCity" let-state="adState">
+  <marquee i18n>
+    Welcome to Mattress City of {{city}}, {{state}}!
+  </marquee>
+</ng-template>
+
+<!-- invoke component with ref to my content template -->
+<ad-component #myComponent [adTemplate]="myTemplate">
+</ad-component>
+
    +
  • + +AddComponent Template + +
  • +
+
+
+
<ng-container *ngTemplateOutlet="adTemplate; context: myObject">
+</ng-container>
+
+
    +
  • + +AddComponent Code + +
  • +
+
+
+
@Input() adTemplate: TemplateRef<any>;
+
+
+
+

Lifecycle hooks

+
+ +
    +
  • + +ngOnInit() {} used frequently + +
  • +
+
+
+
+

Canned event bindings, neat!

+
+ +
+
+
<input type="text" (keyup.enter)="doStuffOnEnter()"/>
+
+
+
+

Modules

+
+

Modules serve 2 main functions:

+
    +
  • + +Importing and exporting dependencies. + +
  • +
  • + +Defining routes and entry points for pages + +
  • +
+
+
+
+

Modules

+
+
+
+
@NgModule({
+  declarations: [
+    /*
+    List of components declared by this module.  Every component used
+    must be declared exactly once throughout the entire application
+    including lazy-loaded modules!
+    */
+  ],
+  imports: [
+    /*
+    List of imported modules.  These modules may be needed by the
+    current module or imported so they can be re-exported (below).
+    */
+  ],
+  exports: [
+    /*
+    List of components and modules made implicitly
+    available to any module that imports this module.
+    */
+  ],
+  providers: [
+    /*
+    List of services available to this module and any
+    module that imports this module.
+    NOTE: @Injectable({providedIn: 'root'}) are globally provided
+    and share a single execution context.
+    */
+  ]
+})
+
+
+
+

Routing: Parameters

+
+
    +
  • + +Routing code + +
  • +
+
+
+
const routes: Routes = [
+  // ...
+  { path: 'record/:id',
+    component: RecordComponent
+  }, {
+    path: 'record/:id/:tab',
+    component: RecordComponent
+  }
+];
+
+
+
+

Routing: Parameters

+
+
    +
  • + +Component code + +
  • +
+
+
+
constructor(private route: ActivatedRoute) {}
+ngOnInit() {
+    this.route.paramMap.subscribe((params: ParamMap) => {
+        this.recordId = +params.get('id');
+        this.recordTab = params.get('tab') || 'copy_table';
+        this.loadRecord();
+    });
+}
+
+
+
+

Routing: Lazy Loading

+
+
+
+
const routes: Routes = [{
+  path: '',
+  component: StaffComponent,
+  resolve: {staffResolver : StaffResolver},
+  children: [{
+    path: '',
+    redirectTo: 'splash',
+    pathMatch: 'full',
+  }, {
+    path: 'login',
+    component: StaffLoginComponent
+  }, {
+    path: 'catalog',
+    loadChildren : '@eg/staff/catalog/catalog.module#CatalogModule'
+  }, // ...
+  }]
+}];
+
+
+
+

Misc.

+
+
+
+
+

Observables

+
+ +
+
+
import {Observable} from 'rxjs/Observable';
+import {map} from 'rxjs/operators/map';
+
+getThings(): Observable<any> {
+    return this.net.request(service, method, arg)
+    .pipe(
+        map(oneValue => {
+            oneValue.bar = 'baz';
+            return oneValue;
+        })
+    );
+}
+
+
+
thing.getThings().subscribe(oneValue => console.log(oneValue.bar));
+
+
+
+

Gotchas: Error handling

+
+
    +
  • + +Fatal errors that prevent routing return to base route. + +
  • +
+
+
+
+

Compiling

+
+
    +
  • + +JIT Compiler + +
      +
    • + +Angular compiler is shipped to the browser. + +
    • +
    • + +Angular templates are compiled at run time. + +
    • +
    +
  • +
  • + +AOT Compiler (--prod) + +
      +
    • + +Templates are pre-compiled to JS. + +
    • +
    • + +About 1/3 of download size. + +
    • +
    +
  • +
  • + +ng-lint + +
      +
    • + +Can also warn on imports + +
    • +
    +
  • +
  • + +--watch + +
      +
    • + +Kill and restart after any major changes (moving modules around, + adding new components, etc.) + +
    • +
    +
  • +
+
+
+
+

My Dev Setup

+
+
    +
  • + +Symlink the build destination directory + +
  • +
+
+
+
ln -s /home/berick/code/Evergreen/Open-ILS/web/eg2/en-US \
+    /openils/var/web/eg2/en-US
+
    +
  • + +Keep a terminal tab open with build running + +
  • +
+
+
+
ng build --watch
+
    +
  • + +Occasionally / after major changes + +
  • +
+
+
+
ng-lint
+ng build --prod # then test
+
+
+
+

Big Questions

+
+
    +
  • + +Configurable Print Templates + +
  • +
+
+
+ + -- 2.11.0