User Tools

Site Tools


angular_2

Angular 2

Komponenty a CIDER pravidlo

  • Při vytváření komponent se řídit mnemotechnickou pomůckou - CIDER
    • Create your class
    • Import your dependencies
    • Decorate your class
    • Enhance with composition
    • Repeat for sub-components

C

class AppComponent { public title = 'Tour of Heroes'; }

I

  • The only dependency we have for AppComponent for now is just Component.

import {Component} from 'angular2/angular2';

D

  • Musím říct Angularu, aby vzal tu mnou vytvořenou třídu AppComponent a “nabindoval” ji na můj my-app element společně s mým my-template.html souborem.
  • říkáme tak, jak se má naše komponenta chovat. Je možné ji nadefinovat styly i direktivy.
 //import {Component} from 'angular2/angular2';
 class Hero {
   id: number;
   name: string;
 }
 @Component({
   selector: 'my-app',
   templateUrl:'my-template.html',
   styleUrls: ['app/app.component.css'],
   directives: [ROUTER_DIRECTIVES]
 })
 class AppComponent {
   public title = 'Tour of Heroes';
   public hero: Hero = {
     id: 1,
     name: 'Windstorm'
   };
 }//

my-template.html:

 <h1>{{title}}</h1>
 <h2>{{hero.name}} details!</h2>
 <div><label>id: </label>{{hero.id}}</div>

E

  • Nyní je možné začít obohacovat komponentu o další věci jako třeba provázání s modelem…
  • We are going to import FORM_DIRECTIVES so that we can have access to ng-model in our template. This next step is very important: we add FORM_DIRECTIVES to the directives arrays.
 //import {Component, FORM_DIRECTIVES} from 'angular2/angular2';
 class Hero {
   id: number;
   name: string;
 }
 @Component({
   selector: 'my-app',
   templateUrl:'my-template.html',
   directives: [FORM_DIRECTIVES]
 })
 class AppComponent {
   public title = 'Tour of Heroes';
   public hero: Hero = {
     id: 1,
     name: 'Windstorm'
   };
 }//

my-template.html po předělání:

 <h1>{{title}}</h1>
 <h2>{{hero.name}} details!</h2>
 <div><label>id: </label>{{hero.id}}</div>
 <div>
   <label>name: </label>
   <div><input [(ng-model)]="hero.name" placeholder="name"></div>
 </div>

R

  • S tím, co jsem udělal předtím, bych v reálném příkladu nevystačil. Jak aplikace roste, je potřeba abstrahovat tyto vytvořené jednotky na sub-komponenty. To je místo, kde CIDER proces jede od začátku - například pokud bych měl nějaký formulář, který disponuje složitější validací a chtěl bych ho oddělit od hlavní komponenty.
  • Opět bych deklarovat novou třídu, která by zapouzdřovala funkcionalitu formuláře, imporotval bych závisloti

Bootstrap v hlavní komponentě

  • vzhledem k tomu, že hlavní komponenta je vstupní branou aplikace, umisťuje se do tohoto místa bootstrap frameworku. Umístíme proto bootstrap do závilsotí a na konci kódu provedeme vyvolání funkce:
 //import {**bootstrap**, Component, FORM_DIRECTIVES} from 'angular2/angular2';
 class Hero {
   id: number;
   name: string;
 }
 @Component({
   selector: 'my-app',
   templateUrl:'my-template.html',
   directives: [FORM_DIRECTIVES]
 })
 class AppComponent {
   public title = 'Tour of Heroes';
   public hero: Hero = {
     id: 1,
     name: 'Windstorm'
   };
 }
 **bootstrap(AppComponent)**;//

Routing v komponentě

  • je potřeba komponentu dekorovat pomocí @RouteConfig
  • To enable routing, we are going to import RouteConfig and ROUTER_DIRECTIVES as well as AboutComponent, ExperimentsComponent and HomeComponent.
 //import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
 import {AboutComponent} from './about/about.component';
 import {ExperimentsComponent} from './experiments/experiments.component';
 import {HomeComponent} from './home/home.component';//

Tvorba routování

  • Potřebujeme routovací moduly a další komponenty pro tvorbu routovací tabulky. Definice routování předáváme v poli do @RouteConfig:
    • cesta routy
    • jméno routy
    • jaká komponenta má být na routu namapována
  • Například chceme domovskou routu mít jako defautní, což nastavíme parametrem useAsDefault: true.
  • základní podstata je v tom, že každá routa se mapuje na komponentu.
 //@RouteConfig([
   {path: '/home',        name: 'Home',        component: HomeComponent, useAsDefault: true },
   {path: '/about',       name: 'About',       component: AboutComponent },
   {path: '/experiments', name: 'Experiments', component: ExperimentsComponent }
 ])//
 

Providers

  • do definice dekorování komponenty přidáme i služby StateService a ExperimentsService
 //
 import {Component} from 'angular2/core';
 import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
 import {AboutComponent} from './about/about.component';
 import {ExperimentsComponent} from './experiments/experiments.component';
 import {HomeComponent} from './home/home.component';
 import {StateService} from './common/state.service';
 import {ExperimentsService} from './common/experiments.service';
 @Component({
   selector: 'app',
   templateUrl: 'app/app.component.html',
   styleUrls: ['app/app.component.css'],
   directives: [ROUTER_DIRECTIVES],
   providers: [StateService, ExperimentsService],
 })
 @RouteConfig([
   {path: '/home',        name: 'Home',        component: HomeComponent, useAsDefault: true },
   {path: '/about',       name: 'About',       component: AboutComponent },
   {path: '/experiments', name: 'Experiments', component: ExperimentsComponent }
 ])
 export class AppComponent {}//
 

Aplikace repeat pravidla z CIDER

  • vytvořím si nový soubor home.component.ts a v něm vytvořím třídu HomeComponent
  • opět importuji závislosti a odekoruji
  • importuji třídu StateService k uchovávání stavu mezi routami. Dependency injection funguje v Angularu 2 v konstruktoru, takže do nově vytvořené třídy jeden přidám a udělám inject pro StateService.
  • Komponenty mají na svůj životní cyklus navázané hookování a můžeme jej použít k sekvenci jejich práce.
    • například chceme získat a nastavit zprávu z StateService poté, co je komponenta inicializována
      • použije se ngOnInit hook
 //
 import {Component} from 'angular2/core';
 import {StateService} from '../common/state.service';
 export class HomeComponent {
   title: string = 'Home Page';
   body:  string = 'This is the about home body';
   message: string;
 constructor(private _StateService: StateService) { }
 ngOnInit() {
     this.message = this._StateService.getMessage();
   }
 updateMessage(m: string): void {
     this._StateService.setMessage(m);
   }
 }//
 

Vlastní StateService, kterou jsme předali přes DI do HomeComponenty

  • Nyní vytvořím StateService, kterou jsem předtím předal jako “providera” v App komponentě.
 //export class StateService {
   private _message = 'Hello Message';
 getMessage(): string {
     return this._message;
   };
 setMessage(newMessage: string): void {
     this._message = newMessage;
   };
 }//
 

Injectable dekorace

  • Tady to začíná být zajímavé. Když chceme učinit StateService dostupnou pro injektnutí v jiných komponentách, prvním krokem je importovat Injectable a pak třídu odekorujeme.
 import {Injectable} from 'angular2/core';
 @Injectable()
 export class StateService {
 

User input události

  • Již se nezachycují pomocí přidávání vlastních angular direktiv, ale prostřednictvím zachytávání nativních DOM událostí a jejich zabalováním do rodičovských věcí. Na příkladu níže je vidět, jak zachytím “click” událost a zavolám metodu updateMessage, která patří komponentě, která tuto šablonu vlastní.

Two-way data binding

  • V Angular 2 jde vlastně o jednosměrný data-binding aplikovaný dvakrát.
  • Lze také bindovat na vlastnosti pomocí závorkové syntaxe.
  • kombinujeme “property binding” (component to view) a “event binding” (view to component) k dosažení two-way data binding.
  • Řešení je překvapivě jednoduché: zabalíme ngModel do “dvouzávorek”
 <input type="text" [(ngModel)]="message" placeholder="Message">

Tady je příklad v daném kontextu:

 //
 <h1>{{title}}</h1>
 {{body}}
 <hr>
 <div>
     <h2 class="text-error">Home: {{message}}</h2>
     <form class="form-inline">
       <input type="text" [(ngModel)]="message" placeholder="Message">
       <button type="submit" class="btn" (click)="updateMessage(message)">Update Message</button>
     </form>
 </div>//
 

Routing markup

  • Když jsme v app.component.ts definovali routy, je potřeba vložit ještě do šablony této hlavní komponenty ty routy také. Vkládáme pomocí router-outlet.
 //<div id="container">
     <router-outlet></router-outlet>
 </div>//
 
  • Pomocí routerLink ve formě [routerLink]=“['/Home']”. Ta hodnota, co se předává do routerLink je jméno routy definované v app.componentě.
 <h1 id="logo">
   <a [routerLink]="['/Home']"></a>
 </h1>
 <div id="menu">
   <a [routerLink]="['/Home']" class="btn">Home</a>
   <a [routerLink]="['/About']" class="btn">About</a>
   <a [routerLink]="['/Experiments']" class="btn">Experiments</a>
 </div>
 

Build přes Gulp

  • založeno na tomto tutoriálu
  • pro komplexnější aplikace je Gulp vhodnější než nástroje z příkazové řádky
  • umožňuje oddělit výstup build skriptu do separátních složek
  • zároveň je možnost ponechat si nástroje příkazové řádky, např. tsc

Přidáme gulp do projektu:

 "gulp": "^3.9.0",
 "gulp-typescript": "^2.8.0",
 "del": "^2.1.0"
 

Vytvořím gulpgile:

 const gulp = require('gulp');
 const del = require('del');
 const typescript = require('gulp-typescript');
 const tscConfig = require('./tsconfig.json');
 // clean the contents of the distribution directory
 gulp.task('clean', function () {
     return del('dist/**/*');
 });
 // TypeScript compile
 gulp.task('compile', ['clean'], function () {
     return gulp
         .src('app/**/*.ts')
         .pipe(typescript(tscConfig.compilerOptions))
         .pipe(gulp.dest('dist/app'));
 });
 gulp.task('build', ['compile']);
 gulp.task('default', ['build']);
 
* všechen zkompilovaný bordel se bude nyní ukládat do složky //dist/app// a už se nebude míchat se zdrojovým kódem
* konfigurace kompilátoru se načítá ze souboru **tsconfig.js**
* zatím se nám tedy buildí do složky //dist// (to dělá Gulp), ale tsc nám stále buildí ke zdrojovým kódům, je nutné to ještě upravit
* docílím tak, že v tsconfig.js přidám ještě parametr **"outDir": "dist/app"**

Sourcemap

  • Unfortunately gulp-typescript does not generate sourcemaps and as a result ignores the sourceMap compiler option in tsconf.js. This can be added via the gulp-sourcemaps plugin.
  • Adding sourcemaps to the build is simply a matter of installing the plugin then wrapping the typescript compile step as follows:
 const sourcemaps = require('gulp-sourcemaps');
 ...
 // TypeScript compile
 gulp.task('compile', ['clean'], function () {
   return gulp
     .src(tscConfig.files)
     .pipe(sourcemaps.init())          // <--- sourcemaps
     .pipe(typescript(tscConfig.compilerOptions))
     .pipe(sourcemaps.write('.'))      // <--- sourcemaps
     .pipe(gulp.dest('dist/app')); 
 });
 

HANDLING STATIC FILES

  • The current gulpfile writes the generated JavaScript files to the dist folder, however, in order to run the application this folder also needs a copy of the HTML, CSS and external libraries.
  • The Tour of Heroes index.html grabs the external libraries direct from the folders where they were installed by npm (in node_modules) and we do not want that!
  • Můžeme proto přidat do gulpfile.js build krok, který zkopíruje závislosti do dist/libs
 // copy dependencies
 gulp.task('copy:libs', ['clean'], function() {
   return gulp.src([
       'node_modules/angular2/bundles/angular2-polyfills.js',
       'node_modules/systemjs/dist/system.src.js',
       'node_modules/rxjs/bundles/Rx.js',
       'node_modules/angular2/bundles/angular2.dev.js',
       'node_modules/angular2/bundles/router.dev.js'
     ])
     .pipe(gulp.dest('dist/lib'))
 });
 

Pak upravím URL v index.html:

 <script src="lib/angular2-polyfills.js"></script>
 <script src="lib/system.src.js"></script>
 <script src="lib/Rx.js"></script>
 <script src="lib/angular2.dev.js"></script>
 <script src="lib/router.dev.js"></script>  
 
* The build also needs to copy the index.html file and any component templates / CSS to the dist folder. Here’s a suitable build step:
 // copy static assets - i.e. non TypeScript compiled source
 gulp.task('copy:assets', ['clean'], function() {
   return gulp.src(['app/**/*', 'index.html', 'styles.css', '!app/**/*.ts'], { base : './' })
     .pipe(gulp.dest('dist'))
 });
 

Dodání live reload

Linting

angular_2.txt · Last modified: 2016/02/06 17:40 by buddha