Angular 2 – Directives

Creating An Index Loop Structural Directive In Angular 2 Beta 14 – Ben Nadel (April 22, 2016)

Advertisements

Unit 2 Notes from Yakov Fain online Angular training, spring 2016

Component router:

  • Allows the user to navigate from one view to another in a SPA.
  • There are two location strategies:
    • HashLocationStrategy – a hash sign is added to the URL and the fragment after the hash sign identifies the route on the client. (Works in all browsers.)
    • PathLocationStrategy – Supported only in browsers that support HTML5.
  • The router is a service implemented by a collection of Dependency Injection providers, most of which are identified in the ROUTER_PROVIDERS array.
  • In the example below,we’re booting Angular 2 with RootComponent as our app’s root component and registering providers in the providers array in the second parameter of the bootstrap function. Providing the router providers at the root makes the Component Router available everywhere in our application.
  • The default location strategy is PathLocationStrategy. You can use the class HashLocationStrategy : bootstrap(RootComponent, [ROUTER_PROVIDERS,  provide(LocationStrategy, {useClass: HashLocationStrategy})]

 

Routing Building Blocks

  • RouterOutlet (<router-outlet>) – where the router should render the component
  • @RouteConfig – Thus decorator maps URLs to components to be rendered inside the <router-outlet>. Note: The preferred way to simultaneously create a router and add its routes is with a @RouteConfig decorator applied to the router’s host component.
  • RouterLink The RouterLink directive lets you link to specific parts of your app. ([routerLink]) – a link to a named route using <a [routerLink] …>
  • RouteParams – a map of key-value pairs to pass parameters to the route
  • RouteData – a map of key-value pairs used to pass additional data from @RouteConfig to a route

Note: An ellipse in a RouteConfig path indicates the child has its own routes.

@RouteConfig([
{path: ‘/’, component: HomeComponent, as: ‘Home’},
{path: ‘/product/:id/‘, component: ProductDetailComponent, as: ‘ProductDetail’ } 
])


 

Lazy loading of routes

In large apps you can improve performance by limiting the amount of initially downloaded code. This can be accomplished by specifying a *loader in the @RouteConfig decorator. When this approach is employed, the underlying component is not loaded until a link to it is clicked on. Here’s an example from unit 2’s Walkthrough 4:

    {path: ‘/luxury’,
            loader: ()=> System.import(‘app/components/luxury’)
                               .then(libModule => libModule.LuxuryComponent), as: ‘Luxury’
 }

The loader for the LuxuryComponent will return a promise.

*Per Theirry Templier’s answer to my 04.10.2016 stackoverflow.com question, Angular2 creates an AsyncRoute when the loader attribute is present in the route definition. He shows the Angular 2 function that does so and notes that the code above gets transpiled into the the same javascript as newing up an AsyncRoute would.

{path: '/luxury',
    loader: ()=> System.import('app/components/luxury')
        .then(libModule => libModule.LuxuryComponent), as: 'Luxury' }

You can take a look at Chrome Dev Tools New work tab before and after clicking on the link to the component to verify this.


 

Child Routes

A child component can have its own routes configured. One of the child’s routes needs to be defined as the default route or Angular 2 will throw an error.

Yakov’s slide 14 in the unit 2 handout shows one way to do this. However, it appears that there is a simpler approach in the Angular 2 Guide’s Router section here. It involves the use of the useAsDefault property on a route definition. Here’s an example from the guide:

{ // Crisis Center child route path: '/crisis-center/...', name: 'CrisisCenter', component: CrisisCenterComponent, useAsDefault: true },

NOTE: As of late April, 2016, I am not able to get useAsDefault to work…

Remember, an ellipse in a RouteConfig path indicates the child has its own routes.


Type Definitions

*.d.ts files declare types for JavaScript libraries and frameworks.

Modules can be declared with the keyword declare, without linking to the actual JS code, These types are called ‘ambient’.

tsc searches for all *.d.ts files in subdirs starting from the root dir specified as rootDir in tsconfig.json.

Angular 2 defines all the type definitions it needs.

If your IDE shows some type in red or with a squiggly red lines under it, that indicates that it can’t find the definition for that type. Solve this by installing the required d.ts files.

Newer type definition manager is ‘typings’. typings can install definitions from various repositories (unlike its predecessor, tsd,which can only install types from definitleytyped.org. See example below:

typings install jquery --ambient --save

Note that without ‘–ambient’ the typings type definition manager will try to find the d.ts file in its own registry (which is at https://github.com/typings/registry.)


How tsc finds modules using the (npm) node module-resolution strategy

Set the module property value to “commonjs” in the tsconfig.json file’s compilerOptions. This will set up tsc to follow the node strategy, which is outlined here:

  • The transpiler will look for ambient declarations (in the node_modules directory) such as
    • import {Component} from 'angular2/core'
  • Then it will check if the path is relative, such as
    • import {Product} from './product.service'
    • tsc will look in the project’s root folder and below for the following in the following order to determine the module and its members’ signatures:
      • a product.service.ts file
      • then, if not found, product.service.d.ts
      • If neither are found, it will look in the node_modules directory

Input and Output properties -Torgeir Helgevold has a nice February, 2016 summary here.

  • Properties marked Input() are for getting data into a child component from the parent. (See slide 26 and/or 1H 25M mark of the video for example.)
    • Note that a parent component will need to include its child(ren) in its @Component decorator’s directives property. Like this:
      directives: [childNameblahblahblahComponent]
    • The parent component passes data to a child using bindings to input properties.
    • The purpose of @Input is to configure data bound input properties with support for change tracking.
  • Properties marked Output() are for sending events from a component.
    • A parent needs the following to listen for an event in a child component:
      • An event-handler function such as:
        priceQuoteHandler (event:IPriceQuote) {
            this.stockSymbil = event.stockSymbol;
            this.price = event.lastPrice;
        }
      • A template or template file that contains a child component’s selector with a property that reference’s the event-handler function in the parent, such as:
        template: `
            <price-quoter (last-price)=“priceQuoteHandler($event)"></price-quoter><br>
            AppComponent received: {{stockSymbol}} {{price | currency:’USD’:true:'1.2-2'}}
        `
  • The child uses instances of the EventEmitter class to send events via Output properties like this:
    @Output ('last=price') lastPrice: EventEmitter <IPriceQuote> = new EventEmitter();
  • The constructor or other function emits the event via the EventEmitter instance like this:
    this.lastPrice.emit(priceQuote);

The Mediator design pattern

See slide 36 & unit2\inter_comp_communications\app\mediator for example

  • The mediator acts as a ‘man in the middle’ and allows for loose coupling of components.
  • A parent component can mediate siblings’ communication therefore the siblings do not need to know about one another. For example, a button on a child component can be clicked to emit data to the parent which can then bind it to a property on another child component.
  • An injected service can mediate communication between any components (an IoC design pattern)

Shadow DOM

  • Every web page is represented by a tree of DOM objects.
  • Shadow DOM allows for encapsulation of a subtree of HTML elements to create a boundary between components.
    • Such a subtree is rendered as part of the HTML document, but its elements are not attached to the main DOM tree.
  • With Shadow DOM the CSS styels of the custom component won’t be merged with the main DOM CSS – nice!
  • encapsulation property of @Component:
    • ViewEncapsulation.Emulated – Emulate encapsulation of the Shadow DOM (default)
    • ViewEncapsulation.Native – Use Shadow DOM natively supported by the browser
    • ViewEncapsulation.None – Don’t use the Shadow DOM encapsulation.

Also, see my additional Shadow DOM links


Dynamically passing HTML to a Child

  • A component can have only one template, but a parent can pass an HTML fragment to a child’s template during runtime. (See slide 40)
  • Add the <ng-content tag to the child’s template to do so.
  • This is called projection (aka ‘transclusion’ in Angular 1)

Unit 1 Notes from Yakov Fain online Angular training, spring 2016

Starting a new Angular project with npm – See slide 46


 

Systemjs – a Universal module loader

  • A ‘module’ is a file
  • Systemjs loads the main module and all its dependencies
  • Systemjs is a polyfill that loads  modules in ES6, UMD, AMD and Commonjs formats.
  • ES7 should include a system object for loading modules.
  • System.config (file or inline) configures the loader
  • System.import() loads modules
    • Example: System.import(‘app’)

 


 

npm – A package manager

  • There are several package managers: npm, jspm, bower…
  • npm comes with the installation of Node.js
  • npmjs.org repo has 200K+ packages
  • To install a package xyz in your project’s dir node_modules:
 npm install xyz
  • To install a package xyz globally:
 npm install xyz -g
  • Typically local installs are configured in the file package.json
  • For packages that you will likely use in many projects, install them globally by typing
    • npm install packageName – g

 

package.json – Used for creating reproduce-able builds!

  • To install package xyz to your project and add it to the dependencies section of package.json, type
    • npm xyz –save
  • To install package xyz and add it to the devDependencies section of the package.json file, type
    • npm xyz –save-dev
  • You can generate a package.json file for your project by typing
    • npm init -y
  • For Angular 2 projects, you will need to add the Angular 2 dependencies to the package.json file:
    • “dependencies”: {
      “es6-shim”: “0.33.13”,
      “es6-promise”: “^3.0.2”,
      “reflect-metadata”: “0.1.2”,
      “rxjs”: “5.0.0-beta.2”,
      “systemjs”: “0.19.22”,
      “zone.js”: “0.5.15”,
      “angular2”: “2.0.0-beta.7”
      },
  • Dependencies are downloaded to the node_modules directory by typing the following at the command line:
    • npm install
  • You will want to have the script tags’ src attributes in the html files’ <head> element reference the files that were downloaded into the node_modules directory when you ran ‘npm install’.

 

Component UI:

  • Component’s markup and styles – keep it inline if it is short or in a separate file if it is long.
  • Use backticks ‘`‘ instead of quote marks to write a multi-line templates within a component

 

Binding Overview

  • Used to keep view and underlying data in sync.
    • If the data changes, the UI changes automatically and…
    • UI changes result in changes to the data
  • UI elements can be bound to a component’s properties
  • Events can be bound to functions and expressions

 

Unidirectional binding – from code to template examples

  • <h1>Hello {{ name }}!></h1>
  • <span [hidden]=”isZipcodeValid”>Not valid</span> – NOTE that hidden is a DOM property and isZipcodeValid is a class variable.

 

Unidirectional binding – from template to code example

  • <button (click)=”placeBid”>Place Bid</button> – NOTE that click is a DOM event
  • <input (input)=”onInputEvent()” />  – NOTE that input is a DOM event
  • In addition to DOM events we can also bind to custom events: <price-quoter (last-price) =”priceQuoteHandler($event)></price-quoter> NOTE that last-price is the custom event.

 

Syntactical sugar to do something like two-way binding

Use [()]

Example: <input [(ngModel)] = “myComponentProperty”>


 

Odds and Ends from the unit1_consultation.mp4 video:

  • We use the angular 2 bootstrap function to load the component that represents the entry point of our SPA. We do this is a main.ts file. That is – it is all it does. We isolate the call to bootstrap here because it gives us flexibility. For example, in the future we create different version that, say, works in NativeScript.
  • Yakov refers to JavaScript hoisting . In JavaScript, a variable can be declared after it has been used. In other words; a variable can be used before it has been declared.
    Hoisting is JavaScript’s default behavior of moving declarations to the top.
  • Relative module names start with “./” or “../”. Rooted module names start with “/”, drive name or name of folder that is child of root.
  • In tsconfig.json – Setting the moduleResolution property’s value to  use the commonjs spec instructs tsc to look in the node_modules directory to resolve Non-relative/non-rooted module names.
  • If you use one of your components within another component’s template or templateUrl, you need to reference the ‘child’ component in the ‘parent”s @Component’s directives declaration. Example:

@Component({
selector: ‘auction-product-item’,
templateUrl: ‘app/components/product-item/product-item.html’,
directives: [StarsComonent]
])

  • {{}} Binding is known as interpolation in ES6. It is the easiest way to display a component’s property’s value in html. With interpolation, we put the property name in the view template, enclosed in double curly braces: {{myHero}} This is used to insert the value into any string.
  • [] Binding is used to bind a value to a component’s property.
  • Note that only the input properties of a component can be updated using property bindings. An input property needs to be included in the the component’s ‘inputs’ array OR declared on the component – the former being sugar for the latter. To illustrate that, both ‘rating’ and ‘rating2’ below will transpile to the same JavaScript:

@Component({
templateUrl: ‘app/components/stars/stars.html’,
styles: [` .starrating { color: #d17581; }`],
selector: ‘auction-stars’,
inputs: [‘rating‘, ‘count’]
})

export default class StarsComponent implements OnInit {
count: number = 5;
rating: number = 0;
stars: boolean[] = [];
@Input() rating2: number = 0;


 

Summary:

  • Typically the TypeScript compiler is configured in the tsconfig.json file
  • The npm install command downloadws all dependencies listed in package.json.
  • In the w4 exercise, we used SystemJS to load and compile the applicaiton.
  • In the w4 exercise, we used SystemJS’s System.import(‘main.ts’) to load the main TypeScript file (and dependencies – if there were any).