Saturday 2 July 2016

AngularJS 2

Design Activities @ Lynda - 


DONE Learning AngularJS 2 Ray Villalobos &
DONE AngularJS 2 Essential Training Justin Schwartzenberger

Angular JS 2 Benefits
  • Framework offering Component Architecture, Services for modularity and reuse, baked in Dependency Injection between common Services and Components decouples business logic from view logic, router module for complex client-side SPAs with parent and child routes between Components. 
  • The first Parent Component of Angular's Component Tree Model is bootstrapped / loaded and the Parent Component's View (HTML Template markup using Template Syntax to allow wiring up HTML Elements DOM events to Component Event Handling Methods, and is rendered with data binded content whereby its associated Component Class defining its application logic provides its functionality, through Properties that represent its Data, and Event Handling Methods) is inspected for any Nested Components and runs each Child Components code down the tree
  • Parent Components may render Child Components (Nested Elements) in HTML Templates using Angular's Component Tree Model whereby each Component is rendered using a selector HTML Tag to associate Component Class logic with.
  • Components create support for new Custom HTML Elements (selector) in the DOM  
Definitions
  • * & # (used to setup Local Temporary Variable) - Syntactic Sugar that framework will interpret and convert to actual syntax
  • Component - Directive with a HTML Template configured with a selector to apply to
  • Directive - Client-side login confgured with intent to apply to a selector with unctionality to transform Existing HTML Elements or Custom HTML Elements in DOM, but they do NOT have a HTML Template
    • Built-In Directives applied to HTML Element
      • ngIf - conditionally rendering DOM elements that directive is on based on expression. Example i.e. in cases when a Property may be empty, we do not want DOM to contain the HTML Element, so we may add ngIf on the container HTML Element. Use <template [ngIf]="true"><div> ...</div></template> HTML Element as Angular does not add <template> to the DOM so even if ngIf evaluates to true you are only left with the contents within the container element
      • ngFor - looping through items to render. Example: i.e. my-item-list.component.html renders list of items, using Component to render them
<section>
  <my-item *ngFor="#myItem of myItems" [myItemToWatch]="myItem" (deleted)="onMyItemDeleted($event)>
  </my-item>
</section>
      • routerLink 
    • Custom Directives applied to HTML Element
      • Attribute written on HTML Element matching selector name. Note that it is not necessary to add Built-In Directives to the selector Component Metadata Property (only Custom Directives)
selector: 'mySelectorName'

<div mySelectorName>
      • Asterix Template Syntax (Syntactic Suger)
<div *ngIf="true">
      • Template Syntax (adding a Directive in an assignment statement)
<div [ngIf]="true">
<div [mySelectorName]="true">
      • Types
        • Structural Directive- modify DOM Layout by adding/removing HTML Elements (ngIf, ngFor, etc)
        • Attribute Directive - change behaviour/appearance of HTML Elements in DOM that they are attached to  (ngClass, etc) by attaching CSS classes to the DOM elements. ngClass takes an Object statement [ngClass]={ 'medium-css-propertyA': myItem.medium === 'A', 'medium-css-propertyB'myItem.medium === 'B' } as a value, with CSS classes as Property Names. Note: Dashes are not allowed in the Property statement unless surrounded by quotes. Example: Based on the myItem element medium Type (i.e. we want to check if myItem.medium Property is value1 or value2 for each CSS class and use it if set to true)
    • Custom Attribute Directives applied to HTML Element
      • Example: Setting a Class on <my-item> HTML Element when set to a certain value or not
        • Import the Directive Decorator 
        • Use @Director Decorator passing it Directive Metadata Properties
        • Set the selector Property so it can find a match on a HTML Element Attribute
        • Add Domain Prefix (i.e. short for 'my watcher') to Directive selector unique to application (i.e. Angular does this with its own 'ng' Domain Prefix)
        • Export Class to be applied to the Host Element that the Directive is on. Import and use the Angular Host Binding Decorator to bind the Host Element Property to the Directive Property (taking a string parameter representing the property syntax). Use it for example to set a CSS Class named is-Favourite 
        • Import Directive into Component Class and Component Metadata Directives Property Array. 
        • Test in Chrome Inspector to check attribute added to DOM HTML Element
process/typescript/favourite.directive.ts
...
import{Directive, HostBinding} from 'angular2/core';
...
@Directive({
    selector: '[mwFavourite]'
})

export class FavouriteDirective {
    // @HostBinding configures the isFavourite Property of the Favourite Direction and targets properties, controlling whether the class is-Favourite is added to the Host HTML Element (with the Directive on it)class is native DOM property on HTML Elements. Class Property is isFavourite and is initialised
    @HostBinding('class.is-Favourite') isFavourite = true;
}

builds/development/partials/my-item.component.html
// HTML Element with mwFavourite Attribute. 
<svg mwFavourite ...

process/typescript/my-item.component.ts
// let Component Class know of Directive to be aware of
import{FavouriteDirective} from './favourite.directive';

// add the favourite directive to the Component Metadata Directives Property Array value
@Component({
    selector: 'my-item',
    directives: [FavouriteDirective],

    • Custom Directive supporting Input values from the Directive Property (on the Host HTML Element) matching the Directive's selector
      • Update Directive to support Directive Property Syntax so we can use the mwFavourite selector of the item element. We set mwFavourite to a statement that evaluates the isFavourite Property by wrapping mwFavourite in brackets [mwFavourite] and assigning it to "my-item.isFavourite" to capture the mwFavourite property in the Favourite Directive (since data gets delivered to Components via Property Bindings when Components use the @Input Decorator) 
      • Add the @Input Decorator on a Directive Class Property (same way as it is used on Component Class Properties that include a template).  Using a Directive is key for dealing with bulk.
      • Note: The @Input Decorator can be used to decorate a Class Property, and it may also be used to decorate a Class Setter Method (ES 2015), which gets called when a property having the same name is set to a value from instance of a class.
builds/development/partials/my-item.component.html
// HTML Element with mwFavourite Attribute. Uses the my-item.isFavourite Property
<svg [mwFavourite] ...

process/typescript/favourite.directive.ts
...
import{Directive, HostBinding, Input} from 'angular2/core';
...

export class FavouriteDirective {
    @HostBinding ...
    // @Input Decorator handler should use name of item it decorates (i.e. so we name the method mwFavourite). The Setter Method gets passed a parameter named Value and {} to finalise the function signature. 
    // AJS2 will match a Property Binding with name mwFavourite, evaluate the statement it is set to, and pass the result of the statement into this Setter Method
    @Input()
    set mwFavourite(value) {
        // Set to parameter received, which is a boolean value (from the my-item.isFavourite Property in my-item.component.html)
        this.isFavourite = value;
    }
}

    • Host Listener Decorator to respond to Host Element Events (i.e. toggle CSS class)
process/typescript/favourite.directive.ts
...
import{... HostListener} from 'angular2/core';
...

export class FavouriteDirective {
...
    @HostBinding('class.is-favourite-hovering') hovering = false;
    @HostListener('mouseenter')
    onMouseEnter() {
        this.hovering = true;
    }
    @HostListener('mouseleave')
    onMouseLeave() {
        this.hovering = false;
    }
...


IN PROGRESS up to 3.6
  • Pipes - Reusable as NOT embedded in Component Classes, takes input Data transformed through logic to output
    • Built-In - date, uppercase, lowercase
    • Custom
  • Data Binding
    • Template Syntax Elements
      • Interpolation
      • Built-in Directives
      • Constructs
        • Template Expressions and Statements
        • Value Binding (for Property, Attribute, Class, and Style)
        • Event Binding
        • Template Expression Operators
      • Patterns
      • Local Template Variables
        • Hash # gets reference to HTML Element to be applied in any Child/Sibling HTML Element in the View to wire up data
<input #myInput1>
<input #myInput2>
<button (click)="myInput1.value = myInput2.value">

  • Form Module
    • Loaded with Directives and Services to help build HTML Forms
      • Data Binding
      • Tracking Changes
      • Validation
      • Air Handling
  • Dependency Injection (DI) == Inversion of Control (IOC)
    • Steps
      • Service Registration - inform Angular in the bootstrap call or in the Component Directive Metadata Decorators of Services classes / values to be handled for us and injected into the Angular App, using Angular's Provider Class, Provide Helper Function, or Providing the Type
      • Retrieval of Services - inform Angular from within Class Constructor Signatures that you want specific Constructor Parameters to be of a certain Type and injected constructor() {} using TypeScript annotations, or Angular Inject Decorator @Inject. Angular then takes over and collects unique Services Providers, queues them into a list and stores them as Singletons in memory (client-side app runs whilst user interacts), and retrieves instances of these classes/values when it executes constructors of classes. Services Registered at Bootstrap are available down the entire Component Tree. Services are available in children below Component-level they were registered on. Custom Classes require informing Angular to perform Constructor Injection by use of the @Injectable() Decorator
    • Uses: Reusable Business Logic for modularity ad single responsibility and mock testability (without dependencies)
    • Architect decoupled Modules with other Modules they depend upon for ease of Unit Testing. Angular handles constructing instances of dependencies where injected. Used in Constructors (i.e. for Components, Directives, Pipes, Services, etc)
    • Declare Types on Constructor Parameters using TypeScript to ensure instances of a certain type are received when constructor run Constructor(myParamTypeForDependency: MyDependency)
    • Component Metadata Properties (for Directives and Providers) uses DI such as selector for @Component Decorator
    • Bootstrapping the AJS2 app to setup Dependency Graph bootstrap(App, [FirstServiceV1, SecondService]) sand then swapping the dependency with a different version for a specific Component
  • Services
    • Reusable Class or function that encapsulates logic to serve application
    • Component and Directive Class logic should only broker data to/from View and adding functionality to the View 
    • Service Class should contain application business logic. Components may specify use of Service via Dependency Injection and call properties and methods from that Service to be made available in the View
    • Service Classes may leverage DI by creating Constructors with specific Parameter Types (using TypeScript) so Angular injects the appropriate dependencies (decoupled from Angular framework, so do not need to know if the dependency is Angular specific)
  • Data Persistence
    • Session Data Storage - In-Memory Data Store using DI
      • Create Class to store data, provide data to Local Storage Service add(item) using Services Pattern, use Constructor DI where needed to bring in instance of object, and then do read/write updates get()
    • Server Data via API
      • Built-in Angular 
        • Persist data to/from API by using HTTP Protocol $http (using HTTP Module from Angular framework) using RESTful calls using JSON data by passing a URL and JS object to Angular HTTP Function and subscribe to results, either using: XML HTTP Request, XHR, or JSONP
  • Routing
    • Handle URL requests client-side (rather than sending them to the server) and adjusting data displayed in UI
    • Built-in - AngularJS Router Module supporting 
      • Route Config to pass to Components
      • routeParams to add variables to URL
      • Routing Links Directive
      • Router Outlets Directive specifying where in HTML Template the routed Component will display
      • Child Routes creation
      • Routing Lifecycle Event Hooks to respond to routing Events
      • Navigation at Interception is handled by interpreting a URL request using route logic to find match and changing Component Tree http://xyz.com/abc. History State handled to alter how browser handles URL by default http://xyz.com/abc/12, so Back/Next user changes result in Angular route changes
    • Setup
      • Update index.html Head Tag to use the Router bundle script 
      • Import ROUTER_PROVIDERS when bootstrapping app
      • Import RouteConfig Decorator to app entry point in app.component.ts and add @RouteConfig before or after @Compont, and pass in an Array of Route Definitions, each with at least a Path (string) and Component (type)
      • Route Parameters that are defined in the path Property and provided in a RouteParams object for injection or use to construct in Components (i.e. /:medium, to load a list filtered by medium)

index.html
<base href="/">
<script src="../node_modules/angular2/bundles/router.dev.js"></script>

main.ts
import {ROUTER_PROVIDERS} from 'angular2/router';
...
bootstrap(AppComponent, [
    ROUTER_PROVIDERS
])

Router does not appear to be not supported by Ionic 2 until AJS 2 Router is finalised

app.component.ts
import {RouteConfig} from 'angular2/router';

@RouteConfig([

    // route for MyItemList using Route Parameters 
    {   path: '/:medium', component: MyItemListComponent, name: 'List' },
    // 
    {   path: '/add',
        component: 'MyItemFormComponent', name: 'AddMyItem' }
])
@Component ...

      • Import ROUTER_DIRECTIVES to use @RouteOutlet Directive (structural directive that targets HTML Element with the name router-outlet) to render the Components after matching a Route. Usage of @RouteConfig Decorator with a @Component creates a router for the component and components will be loaded at router-outlet HTML Template using its Route Definitions 
app.component.ts
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
...

@Component({
    ...
    // replace directives metadata array with ROUTER_DIRECTIVES, since routing will handle them for loading them
    directives: [ROUTER_DIRECTIVES],
    directives: [MyItemListComponent, MyItemFormComponent],
    ...
})

app.component.html
// location to load the Component based on the matched route address entered in the browser (test by entering different routes in browser and Chrome Inspect Element)
<router-outlet></router-outlet>
<my-item-form></my-item-form>
<my-item-list></my-item-list>

      • Import ROUTER_DIRECTIVES to use @RouteLink Directive (handles changes in behaviour of HTML links to wire up navigation alongside the router) 
      • Apply the @RouteLink Directive Property Binding to a link in the view for viewing all my items and assign it to a router DSL string in slash notation followed by the route name defined in route config (i.e. /  points to Root Route; ../ points to Routes of the Parent Component; ./ points to Routes Config of Current Component)
      • Import RouteParams and inject it into out MyItemListComponet Class using a private modifier. When we click links it will filter the contents of what is displayed
app.component.html
// dynamically set the Route Param "medium" for our List Route, add another  link to the array for the routerLink Directive Statement
<a [routerLink]="['./List']", { medium: '' }]"></a>
<a [routerLink]="['./List']", { medium: 'MyRoute1' }]"></a>
<a [routerLink]="['./List']", { medium: 'MyRoute2' }]"></a>

my-item-list.component.ts
// routeParams Class is created/filled when route is run and may be used to deal with Route Parameters
...
import {RouteParams} from 'angular2/router';
..
export class MyItemListComponent {
...
    constructor(private myItemService: MyItemService, private routeParam) {}

    ngOnInit() {
        this.medium = this.routeParams.get('medium');
...
}
      • Add the AddNewMyItemLink. Add RouterLink Decorator to the link. We use ../ since we are in a Component that is rendered as outcomes of the route in the App Component (Parent). 
      • Import and add to Component Metadata
      • Test in browser by clicking the AddMyItem link, and the Form will display instead of the List items
my-item-list.component.html

<a [routerLink]="['../AddMyItem']"></a>

my-item-list.component.ts
...
import {RouteParams, ROUTER_DIRECTIVES} from 'angular2/router';
..
@Component({
    ...
    directives: [MyItemComponent, ROUTER_DIRECTIVES],
...


      • Router Class for router navigation is used within code (not in a template), for use when want to route to a location after the submission of a form for example. Add Router as a parameter of the Constructor using a Private Access Modifier and type Router

my-item-list.component.ts
...
import {Router} from 'angular2/router';
..
export class ...
...
    constructor(..., private router: Router) { }
    ...
   onSubmit(myItem) {
      this.myItemService.add(myItem).subscribe( () => {
       
this.router.navigate(['../List', { medium: myItem.medium } ]);
      });
   }

  • Decorators - Expressions that evaluate to functions for Annotation of classes at design time, and help configure ES 2015 classes. @Component({ }) (@<decorator_name>(<args>)) tells Angular to treat the Class as a Component. TypeScript supports Decorates using its Transpiler. Component Decorator takes an Object Literal { } with known Properties (Component Metadata, including selector, template, or templateUrl) to configure class as an Angular Component.
    • Component Decorator @Component({ })
    • Input Decorator @Input()
      •  @import {Component, Input} from 'angular2/core';
      • Use Input Decorator on a Class Property so Angular supports any Property Bindings used on instances of the selector elements having Property Name myItem
process/typescript/app.component.ts
...
import{MyItemComponent} from './my-item.component';
...
export class AppComponent {
    // sample data set to Object Literal
    sampleMyItem = {
        id: 1,
        name: 'abc'
    }
}

process/typescript/my-item.component.ts
@Component({
    selector: 'my-item',
..
})

export class MyItemComponent {
      @Input() myItem;
}

builds/development/partials/app.html
...
// receive and sample data sampleMyItem (binding Source) using Property Binding Syntax (Properties decorated by the @Input decorator on a Component or Directive that applies to the selector HTML Element) to the @Input name myItem (binding Target) from MyItemComponent
<my-item [myItem]=""sampleMyItem"></my-item>

builds/development/partials/my-item.component.html
...
// interpolation using binding sources
{{ myItem.id }}
{{ myItem.name }}
      • Custom Alias - instead of Property Name exposed for use by other Components
process/typescript/my-item.component.ts
export class MyItemComponent {
      @Input('myItemToWatch') myItem;
}

builds/development/partials/app.html
...
// apply Alias
<my-item [myItemToWatch]=""sampleMyItem"></my-item>
    • Output Decorator @Output()
      • Use to expose Event Bindings on Components to provide notifications from UI
      •  @import {Component, Input, Output} from 'angular2/core';
      • Pass 'deleted' as the Alias
process/typescript/my-item.component.ts
export class MyItemComponent {
      // allow to raise an event
      @Output('deleted') delete = new EventEmitter();

      // raise output event using the emit Method of EventEmitter
      onDelete() {
          // pass 'null' to not have anything returned from event, or alternatively have it pass back what was requested to delete
          this.delete.emit(this.myItem);
      }
}
      • Set the Property to an Event Emitter object by importing it.
      • @import {Component, Input, Output, EventEmitter} from 'angular2/core'
builds/development/partials/app.html
...
// use the event by adding the deleted Alias Event Binding to the my-item HTML Element and set to a Statement to call the onMyItemDeleted method, and access the Emit Value of event using $event
<my-item [myItem]=""sampleMyItem" (deleted)="onMyItemDeleted($event)"></my-item>


process/typescript/app.component.ts
export class AppComponent {
    onMyItemDeleted(myItem) {
        // todo
    }
    // sample data
    sampleMyItem = {
        id: 1,
        name: 'abc'
    }
}

  • Differences b/w AJS1 and AJS2
    • Components (prev. AJS1 Custom Directives)
      • Declarator (with info about Controller)
      • View (template to show)
      • Controller (JS to add functionality to the View)
      • Create functionality around Custom Tags
      • Custom Tags (similar to Web Components). (prev. AJS1 used ng-app directive)
      • Component Architecture (No Controllers)
      • Complex Templating System (No Scope. prev. AJS1 used for communication b/w templates and controllers)
      • Piping Data (prev. AJS1 referred to it as Filtering)
    • Language Choice
      • TypeScript (JS superscript + ES6)
        • Classes (Object-Oriented)
        • Templates
        • Types
        • Annotations (code metadata and Extending Components)
      • ES5 (JS)
      • Dart
    • Build Tool (for TypeScript)
      • GulpJS (or Webpack)
        • Convert TypeScript / ES6 to regular JS as not supported in modern browsers)
        • Sass, Post-CSS, Minify HTML/JS
      • gulpfile.js
      • tsconfig.js (setup options for converting TypeScript to JS)
  • Abbreviations
    • AJS1 - AngularJS 1
    • AJS2 - AngularJS 2
  • Benefits
    • 3-5x faster than AngularJS 1
    • Built with TypeScript (access to Classes)
  • Setup
    • Install Git
    • Install NPM
    • Install TypeScript npm install -g typescript
    • Install Gulp npm install --global gulp-cli
    • Clone Codebase
      • git clone --bare https://github.com/planetoftheweb/angular2.git .git
      • git config --bool core.bare false
      • git reset --hard
      • git branch
      • git checkout 02_07b (beginning)
    • Specify Project Dependencies package.json
      • Libraries (i.e. AJS2, ES6, RxJS)
      • Gulp filters i.e. gulp, gulp-sourcemaps, etc
    • npm install (install all dependencies for the project into node_modules/ directory)
    • Setup Plugins to access features in Build Process
      • Import Plugins into variables within gulpfile.js
      • Gulp File
        • Tags (with Input and Output) i.e. gulp.task('html'...
        • Gulp process automatically moves files From folders process/typescript/ (location of files for TypeScript) To builds/development/ (location of files ready for Production)
        • Tasks
          • For Production - Minimise HTML, process CSS with Post-CSS
          • For Production - Copy List of Libraries (script files for AJS2 to work such as Polyfills and Shims so AJS2 and TypeScript features like Classes and Objects will work in older browsers, and a Module loading library system.src.js used for organising scripts, and RxJS for Observables and reactive programming Rx.js) from node_modules Folder to development/js/lib/angular2/
            • Additional Libraries (HTTP, Router) may be added
          • Process TypeScript (read TS files, process throgh Source Maps, process through TS and load tsConfig.compilerOptions, output Source Maps and processed files to the js/ folder)
          • Watch Tasks and Re-Run Tasks (re-process TS changes) upon Detected Changes and automatically update Browser Preview (monitor launched processes files/folders changes)
          • Setup Web Server (preview site in real-time and handle AJAX calls locally)
          • Default Task is set to the list of all the tasks and runs in sequence when executed from terminal gulp.task('default'...
    • gulp (runs from terminal project automation)
    • Setup HTML Folder Structure & JS Script Tags (Source Files)
      • Update HTML Header with JS Script Tags (matching the list of Copied Librarys in gulpfile.js that are moved from node_modules to development/js/lib/angular2/)
        • Add script src="" for each
      • Update HTML Header with Setup System Configuration File with JS Script Tag (associated with Module loading library system.src.js)
        • Pass the System object a config object script... System.config({... 
        • Setup format for how to import Modules (i.e. 'register' format) script... System.config({... packages:
        • Create file to be loaded from js/ directory script... System.import('js/boot'... (i.e. boot.js, but we have set defaultExtension to 'js'). Loads first before HTML Header script files!
          • Add Promise to errors go to console .then...
        • Add boot.ts into the process/typescript/ Folder
        • Additional AJS2 Script Libraries may be added to HTML Header
    • Import Libraries required to Add Functionality to AJS2 (Modules associated with Libraries as listed in API 2.0) 
      • Import Component definition (to create Components)
        • API 2.0 > angular2/core -> Component
      • Initialise the AJS2 App
        • API 2.0 > angular2/platform-browser > bootstrap (DOES NOT EXIST ANYMORE!!)
    • Component creation
      • Import Library (using AJS2 API to add functionality)
        • Update boot.ts File in process/typescript/  to import Libraries i.e. import {Component} from 'angular2/core'; ...
          • angular2/core - component - Adds Components functionality
          • angular2/platform/browser - bootstrap Initialise application functionality
        • Note that SystemJS loader is configured to load filepaths without their extension import {AppComponent} from './app.component';
      • Create Component elements (with Decorator @)
        • i.e. @Component ({ }) (and pass along object with different decorations using Decorator)
        • Decorator @ - Sets up Info about Components. When AJS2 creates a Component it creates a Shadow DOM Element for it, and loads into it a Template, which allows creation of custom HTML Tags, and subsequent definition of Tag Functionality using JS. 
          • @selector added into Declarator block of @Component (name of HTML Tag to create) selector: ''  (add this Tag value in our HTML code wherever we want the associated template: '' appear)
          • View (view comprising HTML that is shown and replaces the contents of the @selector HTML Tag) or Template template: '' in the Component Decorator
          • Templates:
            • @Component ({ ..., template: ... })
          • Note: Decorators are a JavaScript 7 addition to TypeScript (expressions that return a function) and errors occur if a semi-colon added at the end @Component ({});
        • Controller (for Module and to Bootstrap the application) is JS class defining functionality for the View class AppComponent { }
    • Bootstrap the App (Initialise Loading) using the Bootstrap function bootstrap(AppComponent) passing AppComponent class (imported from API angular2/platform/browser)
          • Finds @selector in HTML
          • Binds to @selector info from the Module
    • Bootstrap Multiple Apps (each to handle different parts of page with their own private info) using separate Boot Module and App Module with different modules importing/exporting info
      • App Component app.component.ts - Generates Functionality for the app in its own Module (separate from bootstrapping Module) and Exports App Component @Component info 
      • Bootstrapping boot.ts file is separated into its own Module that Imports one or more App Components @Component and any other Components the app needs to bootstrap it. boot.ts is then imported into the index.html file with System.import('js/boot')
    • Template Types (in AJS2)
      • Single Line Template Selector Property represents a HTML Tag to be replaced with the Template Property value of a Component Decorator (i.e. @Component ({ selector: 'my-app', template: '<h1>Welcome</h1>' }) )
      • Multi-Line Templates of Complex HTML (using ES6) Use 'backticks' instead of quotes to keep Template and View together improving debugging. Benefit is debugging as all in same component file (i.e. 

process/typescript/app.component.ts
@Component ({ 
    selector: 'my-app', 
    template: `
        <h1>Welcome</h1>
        <p>Paragraph</p>
    ` 
}) )
      • External "Partials" Files HTML Template stored in Partials folder /builds/development/partials are referenced with templateUrl. Benefits of separate Template is Code Completion and Syntax Highlighting are backtick not recognised by all IDEs
process/typescript/app.component.ts
@Component ({ 
    selector: 'my-app', 
    templateUrl: 'partials/app.html'
})

builds/development/partials/app.html
<h1>Welcome</h1>
<p>Paragraph</p>
    • One-Way Data Binding - Binding Component Class to Templates Approaches:
      • Directive Micro-Templates
      • Interpolate Expression - Bind a Component Class Property to a Template

<!-- Option 1: Standard Interpolation Notation -->

<h2>{{ activityName }}</h2>
      • POPULAR Square Bracket Notation - Modify DOM Property of the HTML Element (Angular evaluates expression for the quoted property)

<!-- Option 2A: Square Bracket Notation -->

<h2 [innerHTML]="'name: ' + activityName"></h2>
      • Square Bracket with Interpolation without Square Bracket Alternative
<!-- Option 2B: Square Bracket Notation -->
<h2 innerHTML="'name: ' + {{ activityName }}"></h2>
      • Bind Notation
<!-- Option 3: Bind Notation -->
<h2 bind-innerHTML="'name: ' + activityName"></h2>

process/typescript/app.component.ts
export class AppComponent {

// Formal Notation in ES6 and Class-based Architectures

// Variable Property of Component Declarations of specific strict type
orgName: string;
orgServices: string[]; // Array
orgMedals: any; // Object of type any

// Constructor/Initialiser function to auto setup instance value of Component
constructor() {

// Data Structures
this.orgName = 'Race'; // Property

// Array
this.orgServices = ['Social Networking', 'Record Results'];

// Array of Objects containing Properties
this.orgMedals = [
{
activity: 'Running',
kinds: ['Normal', 'Trips']
},
{
activity: 'Swimming',
kinds: ['Normal', 'Near-Drownings']
},
{
activity: 'Cycling',
kinds: ['Normal', 'Highside', 'Lowside']
},
{
activity: 'Triathalon',
kinds: ['Normal', 'Exhaustion']
}
];
}

}

builds/development/partials/app.html
<!-- Template Syntax -->
<h1>Welcome {{ orgName }}</h1> 

<!-- Unordered List with Items -->
<ul>
   <!-- ngFor Directive Micro-Template. Use 'let' (instead of deprecated hashtag #) for Local Template Variable temporary (iteration variable) used with the Property for the sequence of events -->
<li *ngFor="#service of orgServices">
<!-- ngIf Directive Micro-Template -->
<span *ngIf="orgServices.length > 0">{{ orgServices }}</span>
</li>
</ul>

<ul>
<li *ngFor="#medal of orgMedals">
<span>{{ medal.activity }}</span>
<span>{{ medal.kind }}</span>
</li>
</ul>

    • Event handling Approaches (respond to user input):
      •  One-Way Data Binding - Events captured to perform Action (Method of Class)
        • $event object information passed using AngularJS hooks into a local variable of component method class. Use Chrome Inspector to view $event Properties tracked by AngularJS 2. 
        • Alternatively just pass Temporary Template Variable to onClick Event Handler
process/typescript/app.component.ts
export class AppComponent {
        // Angular JS 2 event tracking. Passing an event is treated differently 
// from passing a Local Template Variable

onClickService(service) {
this.activityName = service;
};

onClickMedal(e) {
// console.log(e.target.innerHTML);
this.activityName = ''; // Reset
this.activityName = e.target.innerHTML;
};

addService(newService) {
console.log('Added Service: ' + newService);
this.orgServices.push(newService);
};
}

builds/development/partials/app.html
<h3>
    <label>Service: 
      <!-- Create Local Template Variable on input field and bind value to an event so AngularJS 2 runs change detection. Additionally bind on blur event when user leaves input field (by pressing Tab key) and when enter is pressed. Clear the input field value after each event. --> 

        <input #newService (keyup.enter)="addService(newService.value); newService.value = '';" (blur)="addService(newService.value)"
       <button (click)="addService(newService.value); newService.value = '';">Add</button> 
    </label> 
</h3>

<ul>
      <li (click)="onClickService(service)" *ngFor="let service of orgServices">
  <!-- ngIf Directive Micro-Template -->
  <button *ngIf="orgServices.length > 0">{{ service }}</button>
      </li>

</ul>
      • One-Way Data Binding - Event may Pass HTML Element using Local Template Variable
process/typescript/app.component.ts
onClickMedal(e, medalItemElement) {
// console.log("medalItemElement is: " + medalItemElement);
medalItemElement.style.backgroundColor = "#ffee55";

};

builds/development/partials/app.html
<ul>
<!-- Click Event passes Local Template Variable called medalContainer (<li> HTML Element) to Event Handler -->

<li #medalContainer (click)="onClickMedal($event, medalContainer)" *ngFor="let medal of orgMedals">
<button>{{ medal.activity }}</button>
<button>{{ medal.kinds }}</button>
</li>
</ul>

    • Two-Way Data Binding - Elements that both Respond to Events and Modify Component Properties at same time. Use this instead of:
      • One-Way Data Binding (Previously)
        • Component Class to Template - HTML Element Properties in Template assigned values based on variable value in Component Class
        • Template to Component Class - Attach Events from HTML Element to Component Action Method
process/typescript/app.component.ts
export class AppComponent {

// Simple Notation

name = 'Luke';

activityName: string = '';

builds/development/partials/app.html
<!-- Prepopulate input field value using Square Bracket Notation. Event Handler detects change of user input field value we can grab this dynamically changing value from the $event variable and store it to a variable property of Componet Class -->


<h2 [innerHTML]="'name: ' + activityName"></h2>

<input #newService 
[value]="activityName"

(input)="activityName=$event.target.value" 



      • Two-Way Data Binding (Simple Alternative) - Both access properties and check for events at same time. Use ngModel and ngControl. For instance we just create an ngModel and have it track the variable value
        • ngModel - Use with Input Fields
OLD
<input #newService 
[value]="activityName"
(input)="activityName=$event.target.value" 

NEW
<input #newService 
[(ngModel)]="activityName"
        • ngControl - Use with Checkbox, Radio Button, Popup

    • CSS Template Attachment to Component (similar to attaching HTML Template partials)
      • Approaches:
        • Single Line Text
        • Multi-Line Stylesheet using Backtick
          • Important Note: Stylesheets are controlled and added only for the Component in which they are declared, for use in the associated Template DOM. Chrome Inspector shows they are added in a new Stylesheet Tag within the HTML Head.
        • External Stylesheet Import
          • Common App-wide Stylesheets - defined in style.css
          • Component-specific Stylesheets - defined such as app.css and imported into Component using styles: [ ] or styleUrls: [ ]
        • Important Note: Stylesheets are imported using an Array (differs from HTML Template Attachment)
        • Note: Recommended to only add styles for specific HTML Elements to Global Stylesheets to keep them organised for specific usage only in Components where they are desired
@Component({
  selector: 'my-app',
  // template: '<h1>Welcome</h1>'
  templateUrl: 'partials/app.html',

  // CSS Styles
  styles: [
  // CSS Styles - Straight Quotes to Insert Specific Element
  '.brandColor',

  // CSS Styles - Backtick usage
  `
        .btn-square {
  display: inline-block;
  padding: 0.75em;
  margin-bottom: 0;
  font-size: 1.3rem;
  line-height: 140%;
  text-align: center;
  vertical-align: middle;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    border: 1px solid transparent;
    border-radius: 4px;
    color: #ffffff;
  background-color: #ff5577;
background-size: 18px 18px;
    min-width: 28px;
    min-height: 28px;
    background-position: center center;
    background-repeat: no-repeat;
   -webkit-user-select: none;
      -moz-user-select: none;
       -ms-user-select: none;
        -o-user-select: none;
           user-select: none;
  }
  `
  ],

  // CSS Styles in External Stylesheet
  styleUrls: [
  'css/app.css'
  ] 


})

    • Complex Data Structures using ECMA Script 6 (ES6) and TypeScript Classes

import {Component} from 'angular2/core';



// Interface (Class without any methods) without data details



interface Org {

name: string,
shortname: string,
location: string
}

@Component({
})

export class AppComponent {
orgs: Org[]; // Array of Org Type


constructor() {

        }

}



var ORGS: Org[] = [
{
"name": "Brand 1",
"shortname": "brand1",
"location": "Tokyo"
},
{
"name": "Brand 2",
"shortname": "brand2",
"location": "Sydney"
}
]
    • Child-Components (break up Component into Child-Components)
    • e.g. Create Child-Component to display Data from Data Structure
      • Create Child-Component Tag <org-item></org-item> 
      • Property Binding to pass each item (from Local Template Variable) of Data from Component to a Property of the Child-Component <org-item [orgChildItem]=item></org-item>  
      • Copy/Paste code snippet into ../builds/development/partials/org_items.html
      • Create TypeScript Child-Component process/typescript/org-item.component.ts
        • Import Core Functionality import {Component} from 'angular2/core';
        • Create Decorator Component @Component and declarations, including inputs for the Child-Component inherited from Parent-Component comprising of variable orgChildItem received in from the Parent Component Template Note: AngularJS 2 assignment error occurred when attempted to name this variable org-child-item instead of orgChildItem
        • Import Child-Component into Parent-Component import {Component} from './org-item.component.ts';
        • Pass along imported Child-Component into Parent-Component Decorator so it is informed directives: [OrgItemComponent]
        • Transfer relevant Styles to Child-Component from Parent-Component by updating Child-Component Decorator due to Specificity of dependencies injected styles: ``

builds/development/partials/app.html
<ul>
<li class="cf" *ngFor="let item of orgs">
<!-- Pass Variable orgChildItem from this Component Template to Child-Component Template Partial -->

// eevaluate as an expression <org-item [orgChildItem]=item></org-item>
</li>
</ul>

process/typescript/org-item.component.ts
import {Component} from 'angular2/core';
import {Component} from './org-item.component.ts';

// componentise HTML Elements in org-item.component.ts that match the selector

@Component ({
        directives: [OrgItemComponent],
})

process/typescript/org-item.component.ts
import {OrgItemComponent} from 'angular2/core';

@Component ({
        selector: 'org-item',
        templateUrl: 'partials/org_item.html',
        styles: `
                .
        `
        inputs: ['orgChildItem']
})

export class OrgItemComponent {}

builds/development/partials/org_item.html
<img style="width: 150px; height: 40px;" src="images/{{ orgChildItem.shortname }}" alt="{{ orgChildItem.name }} Photo">
<h3 class="btn">{{ orgChildItem.name }}</h3>
<h5 class="btn">{{ orgChildItem.location }}</h5>

    • Multiple Child-Components
      • Steps: 
        • Create HTML Partial
        • Create Child Component Class
        • Import Child Component Class into Parent Class
        • Add Child Component to Parent Class Directives array
        • Add new Custom HTML Element (Selector) in app.html with Local Variable orgChildDetails to be Injected into org-details.html . Pass orgChildDetails along through a variable called currentOrgItem that would be generated by a Click Event. Use ngIf to display only if currentOrgItem exists
<org-details *ngIf="currentOrgItem" [orgChildDetails]="currentOrgItem"></org-item>
        • Add Click Event to existing org list Temporary Variable item that calls a function onClickShowCurrentOrg(currentOrgItem) in AppComponent and passes the org to show as a parameter
<li (click)="onClickShowCurrentOrg(currentOrgItem);" ...
        • Create  onClickShowCurrentOrg(item) function in Component and declare variable currentOrg in AppComponent

currentOrg: Org;

        

        onClickShowCurrentOrg(item) {

this.currentOrg = item;

};

        • Add ngIf to only show org-items when there is a search query
        • Reset the search query when a user clicks an org-item

<ul *ngIf="query">

<li (click)="onClickShowCurrentOrg(currentOrgItem); query=''" ...


    • Refactor Interface into Separate File (DRY)
      • Create /process/typescript/org.interface.ts and copy all instances into single file and add an export prefix

export interface Org {

name: string,

shortname: string,

location: string

}
      • Import common interface into files where it is used
import {OrgInterface} from './org.interface';

{{ abc.name | date: 'shortDate' }}
// truncate data and chain with uppercase
{{ abc.name | slice: 0: 10 | uppercase }}
      • Custom Pipes
        • Create /process/typescript/search.pipe.ts
        • Import the Pipe class into search.component.ts from the AJS 2 Pipe Library import {Component} from 'angular2/core';
        • Create Pipe Decorator passing an object and pass along Properties.
          • Pipe uses Transform Method that accepts Pipe Data and Pipe Modifier (how to modify the data) arguments, and create the transformation in the method returns a filtered version of the data. 
          • Convert the filtered data to lowercase so the filter is not case sensitive
        • Create Class Definition and Export it

import {Pipe} from 'angular2/core';


// stateless so metadata property not required

@Pipe({

name: 'find'

})


export class SearchPipe {
transform(pipeData, [pipeModifier]) {
return pipeData.filter((eachItem) => {
return eachItem['name'].toLowerCase()
.includes(pipeModifier.toLowerCase()) ||
eachItem['location'].toLowerCase()
.includes(pipeModifier.toLowerCase())
})
}
}

@Pipe({
name: 'categoryList'
})

export class CategoryListPipe {
transform(myItems) {
var categories = [];
myItems.forEach(myItem => {
if (categories.indexOf(myItem.category) <= -1) {
categories.push(myItem.category);
} });
return categories.join(', ');
}
}


        • Import the Pipe class into app.component.ts from the AJS 2 Pipe Library
import {SearchPipe} from './search.pipe';
        • Add Pipe to Pipe Decorator in search.component.ts

@Component({

pipes: [SearchPipe],
        • Update HTML app.html where iterating through orgs and pipe the information through the find Pipe (passing the search query parameter)
<li (click)="onClickShowCurrentOrg(currentOrgItem); query='';" class="cf" *ngFor="let currentOrgItem of orgs | find:query">



  • HTTP Module (dependencies include RxJS, which contains stripped version of Observable) made available at bootstrap for constructor injection


index.html

<script src="../node_modules/rxjs/bundles/Rx.js"></script>
<script src="../node_modules/angular2/bundles/http.dev.js"></script>

main.ts
import {HTTP_PROVIDERS} from 'angular2/http';
...
bootstrap(AppComponent, [
    HTTP_PROVIDERS
])

  • Mock XHR Back-end

main.ts

import {MockXHRBackend} from './mock-xhr-backend';



bootstrap(AppComponent, [
        ...
        // use HTTP service calling instance of mock back-end to handle read/write 
        provide(XHRBackend, { useClass: MockXHRBackend })
]);

  • HTTP GET and URLSearchParams

my-item.service.ts

import {Http, URLSearchParams} from 'angular/http';

import {Injectable} from 'angular2/core';

// different import syntax since RxJS modules are exported differently
import 'rxjs/add/operator/map';
// constructor requesting HTTP type. Decorate the Service Class with @Injectable to inform AJS 2 to perform constructor injection of http as part of its Dependency Injection Plan, add TypeScript 'private' access modifier, use the property initialisation shortcut
@Injectable()
export class MyItemService {
    constructor(private http: Http) { }
         
    get(medium) {
        var searchParams = new URLSearchParams();
        searchParams.append('medium', medium);

        // HTTP.get() returns an Observable of HTTP response, it takes a second argument of an object literal that supports a search property (instance of searchParams)
        // Use RxJS operator map that has been imported to Unwrap the HTTP response objects send back so service returns my-items (not HTTP response object that Component must deal with). map receives one of the response objects in the Observable, so we use an ES 2015 arrow function => to handle. The .json() method of the HTTP response object unwraps the payload and returns a JS object for the mock backend to convert to JSON. HTTP GET Observable is a Cold Observable as it will not execute until there is a call to subscribe on it
        return this.http.get('my-items', { search: searchParams })
            .map(response => {
                 return response.json().my-items;
            });
    }
}

my-item-list.component.ts
..
export class MyItemListComponent {
...
    ngOnInit() {
        this.myItemsService.get(medium)
           // subscribe takes up to 3x args (next, error, completion), we want capture completion representative of success of receiving items, using myItems as parameter, and arrow function followed by a function block
            .subscribe(myItems => {
                  this.myItems = myItems;
            });
    }
...
}

  • HTTP POST
    • Pass into the HTTP POST a JSON Content Type of the MyItem Model received by the Service
    • Import Headers Class type of HTTP Bundle
my-item.service.ts
import {Http, URLSearchParams, Headers} from 'angular/http';
...
export class ...
...
   add(myItem) {
       var headers = new Headers({'Content-Type': 'application/json'});
       return this.http.post('my-items', JSON.stringify(myItem), { headers: headers        })
   }

   delete(myItem) {
       // ES2015 backticks syntax
       return this.http.delete(`my-items/${myItem.id}`)
            .map(response => {});
   }

my-item-form.component.ts
..
export class MyItemFormComponent {
...
    onSubmit(myItem) {
        this.myItemsService.add(medium)
           // 
            .subscribe();
    }
...
}

my-item-list.component.ts
..
export class MyItemListComponent {
...
    onMyItemDeleted(myItem) {
        this.myItemsService.delete(medium)
             // trigger reload of the list after delete
            .subscribe( () => {
                  this.getMyItems(this.medium);
            });
    }
...
}

  • Refactor ControlGroup into FormBuilder
my-item-form.component.ts
..
import {ControlGroup, ....., FormBuilder} from 'angular2/common'
export class MyItemFormComponent {
...
    // constructor + formBuilder parameter with Typing (using TypeScript that compiles to JS to abstract the complexity of implementation). It handles the Types and the Decorator to inform Angular what the Constructor's Parameters should be
    constructor(private formBuilder: FormBuilder) {}
    // store in property on Component class from the Constructor using the Priviate Access Modifier as this causes TypeScript to create the property and set it when it transpiles to JS. Refactor ngnInit to use the injected FormBuilder and call the group method, replacing old ControlGroup statement
    ngOnInit() {
        this.form = this.formBuilder.group({
        this.form = new ControlGroup({
        
    }
...
}

  • Create Custom Service
my-item.service.ts
..
import {ControlGroup, ....., FormBuilder} from 'angular2/common'
// export the class so it can be used elsewhere (reusable)
export class MyItemService {

    // methods to deal with the Data
    get() {
        return this.myItems;
    }
   
    add(myItem) {
        this.myItems.push(myItem);
    }

    delete(myItem) {
        var index = this.myItems.indexOf(myItem);
        if (index >= 0) { 
            this.myItems.splice(index, 1);
        }
    }

    // refactor and move Data from Component to Service
    myItems = [
        {
            id: 1,
           ...
         },
         {
            id: 2,
            ....
         }
     ];
}

my-item-list.component.ts
..
import(MyItemService} from './my-item.service';

@Component({
    ...
    // add Provider's Metadata to Component Property
    providers: [MyItemService],
    ...
}
export class MyItemListComponent {
    ...
    // create constructor function so Angular can perform Constructor Injection
    // give it a private Parameter, and set the Type to MyItemService
    constructor (private myItemService: MyItemService)

    ngOnInit() {
        this.myItems = this.myItemService.get();
    }

    onMyItemDeleted(myItem) {
        this.myItemService.delete(myItem);
    }
...
}

my-item-form.component.ts
..
import {... MyItemService ...} from 'angular2/common'

@Component{
    provider: [MyItemService],
...

export class MyItemFormComponent {
    form;
    constructor( private formBuilder: FormBuilder,
                        private myItemService: MyItemService) {}
...
    onSubmit(myItem) {
        this.myItemService.add(myItem);
    }
...
}

  • Move Service/Provider Registration higher up the Bootstrap level function that targets the highest Component in the Component Tree Dependency Graph (so all Components only use a single instance of it)
main.ts
..
import {MyItemService} from './my-item.service';
...
bootstrap(AppComponent, [
    MyItemService
]);

my-item-list.component.ts
..
import(MyItemService} from './my-item.service';

@Component({
    ...
    providers: [MyItemService],
    ...
}

my-item-form.component.ts
..
import {... MyItemService ...} from 'angular2/common'

@Component{
    provider: [MyItemService],
...
}

  • Create Value Provider for app using Inject Decorator with String Literal (lookup list available at bootstrap)
    • Create New Provider using AJS 2 Framework Helper Function provide
main.ts
..
import {provide} from 'angular2/core';

var lookupLists = {
    mediums: ['MyCategory1', 'MyCategory2']
};

bootstrap(App, [
    MyItemService,
    // arg1: key, arg2: useValue/useClass Property.
    // so now the lookupLists Object is available for injection using the key 'LOOKUP_LISTS'
    provide('LOOKUP_LISTS', { useValue: lookupLists })
)];

    • Inject the Value-Type (LOOKUP_LISTS) into Constructors (such as the Form to help render the drop-down form objects)

my-item-form.component.ts
..
// import Inject Decorator (used to Decorate Function Parameters) whereas some others only decorate class properties
import {Component, Inject} from 'angular2/core

export class MyItemFormComponent {
    form;
    
    constructor(private formBuilder: FormBuilder,
                        private myItemService: MyItemService,
                         // Inject Decorator  supports passing in a String Literal representative of the Data Type. We want AJS 2 to pass in the LOOKUP_LISTS Value Parameter into this Constructor during Constructor Injection. We want to use this LOOKUP_LISTS object so we can render the select options on the form, since all properties on the Component form will be available in the HTML Template. Add 'public' to make lookupLists a Class Property for the Parameter and Setting it in the Constructor
                        @Inject('LOOKUP_LIST') public lookupLists ) {}
}

my-item-form.component.html
..
// replace with LOOKUP_LISTS in an ngFor Directive
   
<select ...ngControl="medium">
   <option *ngFor="#medium of lookupLists.mediums" value="{{medium}}"></option>
   <option value "MyCategory1">MyCategory 1</option>
   <option value "MyCategory2">MyCategory 2</option>
(
    • Opaque Token  (concrete type) - Alternative to using Inject Decorator with String Literal (since using String Literals for Tokens is a risk as harder to track and maintain)
providers.ts
..
import {OpaqueToken} from 'angular2/core';

export var LOOKUP_LISTS = New OpaqueToken('LookupLists');

// move here from main.ts
   
export var lookupLists = {
    mediums: ['MyCategory1', 'MyCategory2']
};

main.ts

// bring in LOOK_UP LISTS token
..
import {LOOKUP_LISTS, lookupLists} from './providers';

// bring in LOOK_UP LISTS token instead of the string literal (just remove the quotes)
bootstrap (AppComponent, [
    MyItemService,
    provide(LOOKUP_LISTS, { useValue: lookupLists })
    provide('LOOKUP_LISTS', { useValue: lookupLists })
]);


my-item-form.component.ts
import {LOOKUP_LISTS, lookupLists} from './providers';
...
export class MyItemFormComponent {
    form;
    
    constructor(private formBuilder: FormBuilder,
                        private myItemService: MyItemService,
                        @Inject LOOKUP_LISTS) public lookupLists) {}
                         @Inject LOOKUP_LISTS) public lookupLists) {}

TODO 5. FORMS


  • Troubleshooting & Solutions
    • Problem: Server node_modules related errors when running AJS 2.0.0-beta.17:
      • error TS2304: Cannot find name 'MapConstructor'.
      • error TS2304: Cannot find name 'SetConstructor'.
      • error TS2304: Cannot find name 'Set'.
      • error TS2304: Cannot find name 'Map'.
      • error TS2304: Cannot find name 'Promise'.
    • Solution:
      • Reference: https://github.com/angular/angular/issues/7280
      • Option 1 - Use ES6 instead of ES5:
        • Change es5 to es6 for compilerOptions target in tsconfig.json
      • Option 2 - Keep Transpiling to ES5 and Install Typings for ES6-Shim:
        • Keep es5 for compilerOptions target in tsconfig.json
        • Update gulpfile.js with:

gulp.task('typescript', function () {

  return gulp

    .src([

      'node_modules/angular2/typings/browser.d.ts',

      tsSrc + '**/*.ts'

      ])

No comments:

Post a Comment