Wprowadzenie do Angular 2 - Instytut Informatyki Teoretycznej i
Transkrypt
Wprowadzenie do Angular 2 - Instytut Informatyki Teoretycznej i
Wprowadzenie do Angular 2 Wprowadzenie do Angular 2 Tworzenie serwisów Web 2.0 dr inż. Robert Perliński [email protected] Politechnika Częstochowska Instytut Informatyki Teoretycznej i Stosowanej 30 maja 2016 1/61 Plan prezentacji 1 Trochę informacji o Angular 2 2 Hello World w Angular 2 Przygotowanie projektu Pierwszy komponent 3 Rozbudowa aplikacji 4 Źródła Wprowadzenie do Angular 2 2/61 Angular 2 https://angular.io/ Warto polecić: http://www.angular2.com/ - zbiór odnośników związanych z Angular 2 https://egghead.io/technologies/angular2 - przewodnik video po Angular 2 Wprowadzenie do Angular 2 3/61 egghead.io - nauka technologii internetowych Wprowadzenie do Angular 2 https://egghead.io 4/61 Cechy i zalety - jeden kod na wszystkie platformy Nowoczesne aplikacje sieciowe: wykorzystanie nowoczesnych możliwości platformy sieciowej pozwala na dostarczanie stron wyglądających i działających jak aplikacje desktopowe, wysoka wydajność, działaie bez dostępu do internetu, brak potrzeby instalacji. Aplikacje natywne: Można tworzyć natywne aplikacje korzystając z takich technologii jak: Ionic Framework - http://ionicframework.com/ Native Script, React Native. Aplikacje na komputery stacjonarne: pozwala tworzyć aplikacje z wersją instalacyjną na Mac, Windows i Linux’a, używamy tych samych metod co w technologiach sieciowych, korzystamy z natywnego API danej platformy. Wprowadzenie do Angular 2 5/61 Cechy i zalety - szybkoś i wydajność Generowanie kodu: Angular przekształca szablony w kod wysoko zoptymalizowany dla dzisiejszych maszyn wirtualnych JavaScript, dostarcza wszystkich zalet kodu pisanego ręcznie z wykorzystaniem produktywności szablonu aplikacji. Universalność: dostarcza wygląd aplikacji zbudowanej w oparciu o node.js, .NET, PHP czy inne serwery, renderuje widok do HTML i CSS niemal natychmiastowo, przygotowuje dobry grunt dla aplikacji pod optymalizację SEO (search engine optimization). Podział kodu: szybkie wczytywanie aplikacji dzięki komponentowi Router (automatyczne dzielenie kodu), użytkownicy wczytują tylko ten kod, który jest potrzebny do wygenerowania żądanego widoku. Wprowadzenie do Angular 2 6/61 Cechy i zalety - produktywność Szablony: szybkie tworzenie widoków (interfejsów użytkownika) dzięki prostej ale potężnej składni szablonów. Angular CLI: interfejs lini komand dla Angular, coś na wzór npm dla node.js, szybkie tworzenie szablonu aplikacji, dodawanie komponentów i testów, natychmiastowe instalowanie/wdrażanie, http://ngcli.github.io/ Zintegrowane środowisko programistyczne: inteligentne dopełnianie kodu, wykrywanie błędów i inne informacje zwrotne w popularnych edytorach. Wprowadzenie do Angular 2 7/61 Cechy i zalety - pełne wsparcie przy tworzeniu Testowanie: środowisko Karma (https://karma-runner.github.io/) dla testów jednostkowych, informacja o błędach przy każdym zapisywaniu projektu. Protractor (http://angular.github.io/protractor) zapewnia szybkość i stabilność scenariuszom testowym. Animacje: tworzenie skomplikowanych choreografi i przebiegów czasowych animacji wysokiej wydajności, wystarczy mała ilość kodu dzięki intuicyjnemu API Angular. Dostępność: tworzenie dostępnych aplikacji z komponentami dla ARIA (aplikacje internetowe wysokiej dostępności, np. dla osób niepełnosprawnych), tworzenie przewodników dla programistów (żeby aplikacje były wysoko dostępne), wbudowana infrakstuktura do testowania dla projektu a11y. Wprowadzenie do Angular 2 8/61 Przytotowanie projektu Przygotowanie projektu czyli prawie Hello World: 1 2 instalacja Node.js i npm - bez tego nie popracujemy utworzenie i konfiguracja projektu: tworzymy katalog projektu: mkdir empty-project cd empty-project dodajemy definicje pakietu i pliki konfiguracyjne: package.json tsconfig.json typings.json systemjs.config.js instalacja pakietów npm install Wprowadzenie do Angular 2 9/61 package.json - rekomendowany zbiór pakietów package.json { "name": "empty-project", "version": "1.0.0", "license": "ISC", "dependencies": { "@angular/common": "2.0.0-rc.1", "@angular/compiler": "2.0.0-rc.1", "@angular/core": "2.0.0-rc.1", "@angular/http": "2.0.0-rc.1", "@angular/platform-browser": "2.0.0-rc.1", "@angular/platform-browser-dynamic": "2.0.0-rc.1", "@angular/router": "2.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.1", "@angular/upgrade": "2.0.0-rc.1", "systemjs": "0.19.27", "es6-shim": "^0.35.0", "reflect-metadata": "^0.1.3", "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", "angular2-in-memory-web-api": "0.0.7", "bootstrap": "^3.3.6" }, "devDependencies": { "concurrently": "^2.0.0", "lite-server": "^2.2.0", "typescript": "^1.8.10", "typings":"^0.8.1" } } Wprowadzenie do Angular 2 10/61 package.json Aplikacje Angular i sam Angular zależy od funkcjonalności dostarczanej przez innych dostawców. Zarządzamy tym z użyciem menadżera NPM. Zbiór pakietów przedstawiony w package.json: dobrze współpracuje ze sobą, zawiera wszystko co potrzebne do zbudowania przykładowej aplikacji i jest to więcej niż potrzeba w wielu aplikacjach, pobranie większej liczby pakietów nie wpływa negatywnie na działanie aplikacji, na serwer wysyła się tylko to, czego aplikacja naprawdę potrzebuje. Zależności: dependencies - pakiety wymagane do działania aplikacji, devDependencies - pakiety wymagane tylko przy tworzeniu aplikacji instalacja pakietów produkcyjnych: npm install hello-app --production Wprowadzenie do Angular 2 11/61 package.json Trzy kategorie wymaganych pakietów: użytkowe (ang. feature) - dostarczają naszej aplikacji podstawowe i użytkowne możliwości, wygładzające (ang. polyfill) - dostarczają funkcjonalności, która nie jest obecna we wszystkich przeglądarkach, inne - pozostałe pakiety wspierające naszą aplikację, np. bootstrap. Wprowadzenie do Angular 2 12/61 package.json Pakiety użytkowe: @angular/core - najważniejsze pakiety dla działania aplikacji, obsługa wszystkich metadanych dotyczących: dekoratorów, komponentów, dyrektyw, wstrzykiwania zależności oraz cyklu życia komponentów i związanych z tym zdarzeń i wywołań, @angular/common - często używane usługi, strumienie, dyrektywy dostarczone razem z Angular, @angular/compiler - kompilator szablonów dostarczany razem z Angular, @angular/platform-browser - obsługa funkcjonalności związanej z DOM i przeglądarką, @angular/platform-browser-dynamic - dostarcza i uruchamia metody pozwalające aplikacji kompilować szablony po stronie klienta, używany do uruchamiania aplikacji podczas jej tworzenia, @angular/http - klient http w Angular, @angular/router - komponent do obsługi routingu, @angular/upgrade - zbiór narzędzi pozwalający zaktualizować aplikację napisaną w Angular 1, system.js - obsługa dynamicznego wczytywania modułów, kompatybilna z ES2015. Wprowadzenie do Angular 2 13/61 package.json Pakiety wygładzające: es6-shim - pakiet zapewnia wsparcie dla najważniejszych cech ES2015(ES6); z czasem przestanie być potrzebny, reflect-metadata - obsługa zeleżności między Angular i kompilatorem TypeScript; aktualizacja wersji zależności TypeScript musi odbywać się niezależnie od wersji Angular, rxjs - Reactive Extensions Library for JavaScript; odpowiada za wybór właściwej wersji typów obserwowanych (ang. Observable type) bez konieczności oczekiwania na wydanie nowej wersji Angular, zone.js - obsługa właściwego kontekstu wywoływania funkcji asynchronicznych; tutaj również chodzi o uniezależnienie tej funkcjonalności od wersji Angular. Wprowadzenie do Angular 2 14/61 package.json Pozostałe pakiety: angular2-in-memory-web-api - biblioteka służąca do symulowania internetowego api, dobra we wczesnej fazie projektu, bootstrap - HTML i CSS framework, ładny wygląd, tworzenie RWD. Wprowadzenie do Angular 2 15/61 package.json - devDependencies Pakiety wykorzystywane przy tworzeniu: concurrently - narzędzie pozwalające uruchamiać wiele poleceń npm jednocześnie, lite-server - lekki serwer internetowy ze świetnym wsparciem dla Angular, wykorzystywany przy tworzeniu aplikacji, typescript - obsługa języka TypeScript i kompilator tsc, typings - menadżer dla plików definicji do TypeScript. Wprowadzenie do Angular 2 16/61 package.json - skrypty package.json - scripts { "name": "empty-project", "version": "1.0.0", "scripts": { "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ", "lite": "lite-server", "postinstall": "typings install", "tsc": "tsc", "tsc:w": "tsc -w", "typings": "typings" }, "license": "ISC", "dependencies": { ... }, "devDependencies": { ... } } Wprowadzenie do Angular 2 17/61 package.json - skrypty Skrypty (uruchamiane poleceniem npm run): npm start - uruchamia kompilator i serwer w tym samym czasie, oba w trybie śledzenia zmian, npm run tsc - jednorazowe uruchomienie kompilatora tsc, npm run tsc:w - kompilator w trybie śledzenia zmian, npm run lite - ruchamia lite-server, npm run typings - uruchamia osobno narzędzie do obsługi definicji dla TypeScript, npm run postinstall - wywoływane automatycznie po udanej instalacji pakietów npm; instaluje definicje pakietów dla TypeScript. Ręczne uruchomienie concurrently: ./node_modules/concurrently/src/main.js "npm run tsc:w" "npm run lite" Wprowadzenie do Angular 2 18/61 tsconfig.json - ustawienia kompilatora tsc tsconfig.json - ustawienia kompilatora tsc { "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false }, "exclude": [ "node_modules", "typings/main", "typings/main.d.ts" ] } Wprowadzenie do Angular 2 19/61 typings.json - pliki definicji dla TypeScript typings.json { "ambientDependencies": { "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654", "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", "node": "registry:dt/node#4.0.0+20160509154515" } } Wprowadzenie do Angular 2 20/61 systemjs.config.js - wersja minimalna (function(global) { // zmienna map informuje system wczytywania pakietów gdzie szukać podanego pakietu var map = { 'myApp': 'folderApp', // 'dist', 'rxjs': 'node_modules/rxjs', '@angular': 'node_modules/@angular' }; // zmienna packages informuje system jak wczytać pakiet jeśli nie podano pliku i/lub rozszerzenia var packages = { 'myApp': { main: 'main.js', defaultExtension: 'js' }, 'rxjs': { defaultExtension: 'js' } }; var packageNames = [ '@angular/common', '@angular/compiler', '@angular/core', '@angular/platform-browser', '@angular/platform-browser-dynamic' ]; // dodaje wpisy o pakietach w poniższej formie: // '@angular/common': { main: 'index.js', defaultExtension: 'js' } packageNames.forEach(function(pkgName) { packages[pkgName] = { main: 'index.js', defaultExtension: 'js' }; }); var config = { map: map, packages: packages } System.config(config); })(this); Wprowadzenie do Angular 2 21/61 systemjs.config.js (function(global) { var map = { 'myApp': 'folderApp', // 'dist', 'rxjs': 'node_modules/rxjs', 'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api', '@angular': 'node_modules/@angular' }; var packages = { 'myApp': { main: 'main.js', defaultExtension: 'js' }, 'rxjs': { defaultExtension: 'js' }, 'angular2-in-memory-web-api': { defaultExtension: 'js' }, }; var packageNames = [ '@angular/common', '@angular/compiler', '@angular/core', '@angular/http', '@angular/platform-browser', '@angular/platform-browser-dynamic', '@angular/router', '@angular/router-deprecated', '@angular/testing', '@angular/upgrade', ]; packageNames.forEach(function(pkgName) { packages[pkgName] = { main: 'index.js', defaultExtension: 'js' }; }); var config = { map: map, packages: packages } System.config(config); })(this); Wprowadzenie do Angular 2 22/61 SystemJS - jak to działa? SystemJS: narzędzie do wczytywania aplikacji i poszczególnych modułów, jedno z wielu, alternatywą może być np. webpack (https://webpack.github.io/), jak wszystkie inne narzędzia, wymaga on konfiguracji, wszystkie konfiguracje narzędzi wczytujących stają się wraz z rozwojem aplikacji skomplikowane, dobrze więc dobrze znać narzędzie, którego się używa, tworzymy więc plik systemjs.config.js. Wprowadzenie do Angular 2 23/61 SystemJS - jak to działa? Konfiguracja SystemJS: najpierw mapy wskazujące miejsce, gdzie SystemJS ma szukać aby zaimportować określony moduł: systemjs.config.js // zmienna map informuje system var map = { 'myApp': 'rxjs': 'angular2-in-memory-web-api': '@angular': }; gdzie szukać podanego pakietu 'folderApp', // 'dist', 'node_modules/rxjs', 'node_modules/angular2-in-memory-web-api', 'node_modules/@angular' następnie rejestrujemy wszystkie nasze pakiety: wszystkie pakiety z których korzystamy, nasz własny (na razie jedyny) pakiet myApp, opis pakietu myApp informuje system co robić, kiedy pojawi się żądanie modułu z katalogu folderApp: import { AppComponent } from ’./app.component’; Wprowadzenie do Angular 2 24/61 Tworzenie kodu - pierwszy komponent Tworzymy katalog dla pierwszego komponentu: mkdir folderApp folderApp/app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '<h1>Witaj w moim projekcie ...</h1>' }) export class FirstComponent { } FirstComponent będzie korzeniem całej aplikacji. Każda aplikacja ma przynajmniej jeden taki komponent. Zwykle nazywa się go AppComponent. Komponenty są podstawowymi blokami konstrukcyjnymi aplikacji Angular. Kontrolują jakiś obszar ekranu - widok - poprzez związany z nimi szablon. Wprowadzenie do Angular 2 25/61 Struktura komponentu Struktura komponentu: folderApp/app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '<h1>Witaj w moim projekcie ...</h1>' }) export class FirstComponent { } jedno lub więcej poleceń import do tego co potrzebujemy, dekorator @Component - mówi aplikacji Angular, jakiego szablonu użyć i jak utworzyć komponent, klasa komponentu, kontroluje wygląd i zachowanie widoku poprzez jego szablon. Wprowadzenie do Angular 2 26/61 Importowanie Aplikacje Angular mają modułową budowę. Składają się z wielu plików, z których każdy ma określone przeznaczenie. Angular sam w sobie jest zbudowany z modułów. Jest kolekcją modułów, każdy składający się z kilku powiązanych funkcji. Kiedy potrzebujemy czegoś z bibliotek - importujemy to: import { Component } from ’@angular/core’; import z podstawowej biblioteki Angular, dostęp do wzorca dekorator. Wprowadzenie do Angular 2 27/61 Dekorator @Component folderApp/app.component.ts @Component({ selector: 'my-app', template: '<h1>Witaj w moim projekcie ...</h1>' }) Component to funkcja dekoratora, pobiera jeden argument - objekt JS zawierający metadane. Wywołujemy tę funkcje na obiekcie klasy poprzedzając ja symbolem @. @Component to dekorator, który pozwala nam związać jakieś metadane z klasą komponentu. Metadane mówią aplikacji Angular jak utworzyć i używać komponentu. selector określa prosty selektor CSS dla elementu HTML - wszędzie gdzie wystąpi znacznik my-app będzie instancja FirstComponent. template - szabon dla widoku - renderowany w czasie tworzenia widoku; Może zawierać zmienne, właściwości komponentu, może się odwoływać do innych komponentów tworząc drzewo komponentów. Wprowadzenie do Angular 2 28/61 Klasa komponentu folderApp/app.component.ts export class AppComponent { } Tutaj klasa komponentu jest pusta, nic nie robi. Później można ją rozwinąć o właściwości i jakąś logikę. Eksportujemy klasę na zewnątrz (export) - można ja zaimportowć gdziekowiek w aplikacji (import). Wprowadzenie do Angular 2 29/61 Plik wejściowy aplikacji folderApp/main.ts import { bootstrap } from '@angular/platform-browser-dynamic'; import { FirstComponent } from './app.component'; bootstrap(FirstComponent); Trzeba gdzieś wczytać utworzony komponent. Importujemy dwie rzeczy: funkcję bootstrap przeglądarki dla Angular’a, główny komponent aplikacji FirstComponent z odpowiedniego pliku/modułu, odpalamy aplikację (bootstrap) z odpowiednim komponentem. Funkcja uruchamiająca aplikację zależy od platformy, nie jest więc w @angular/core. Aplikację można załadować na przykład na tablecie z NativeScript. Podzła na komponent i plik go ładujący jest wzorcowy, zapewnia porządek. Wprowadzenie do Angular 2 30/61 Plik index.html <html> <head> <title>Pierwsza plikacja w Angular 2 </title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="styles.css"> <!-- 1. Load libraries --> <!-- Polyfill(s) for older browsers --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.js"></script> <script src="node_modules/reflect-metadata/Reflect.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <!-- 2. Configure SystemJS --> <script src="systemjs.config.js"></script> <script> System.import('myApp').catch(function(err){ console.error(err); }); </script> </head> <!-- 3. Display the application --> <body> <my-app>Wczytywanie...</my-app> </body> </html> Wprowadzenie do Angular 2 31/61 Zbudowanie i uruchomienie aplikacji Kompilujemy i uruchamiamy aplikację: npm start Można dodać jeszcze plik CSS aby nasz napis lepiej wyglądał. Wprowadzenie do Angular 2 32/61 1. Dodanie pól do klasy komponentu Pierwsza zmiana to dodanie dwóch pól do klasy i wyświetlenie ich w szablonie: folderApp/app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '<h1>{{title}}</h1><h2>{{hero}}</h2>' }) export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero = 'Jan Paweł II'; } Projekt powinien się automatycznie przekompilować. Widok pokazuje pola naszego komponentu. Wprowadzenie do Angular 2 33/61 2. Dodanie klasy zamiast prostych pól Druga zmiana to dodanie klasy zamias prostego pola. W szablonie wyświetlamy pola klasy: folderApp/app.component.ts import { Component } from '@angular/core'; export class Bohater { id: number; name: string; } @Component({ selector: 'my-app', template: '<h1>{{title}}</h1><h2>{{hero.name}}</h2>' }) export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero: Bohater = {id:12, name:'Jan Paweł II'}; } Wprowadzenie do Angular 2 34/61 3. Powiększenie szablonu Trzecia zmiana to powiększenie szablonu, korzystamy z odwrotnego apostrofu (‘): folderApp/app.component.ts import { Component } from '@angular/core'; export class Bohater { id: number; name: string; } @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div><label>nazwa: </label>{{hero.name}}</div> ‘ }) export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero: Bohater = {id:12, name:'Jan Paweł II'}; } Wprowadzenie do Angular 2 35/61 4. Dodanie pola edycji w szablonie Mała zmiana w szablonie, dodajemy pole do edycji nazwy naszego bohatera: folderApp/app.component.ts import { Component } from '@angular/core'; export class Bohater { id: number; name: string; } @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>nazwa: </label> <input value="{{hero.name}}" placeholder="name"> </div> ‘ }) export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero: Bohater = {id:12, name:'Jan Paweł II'}; } Wprowadzenie do Angular 2 36/61 5. Dwukierunkowe wiązanie danych Pobieranie nazwy bohatera z użyciem dwukierunkowego wiązania danych: folderApp/app.component.ts import { Component } from '@angular/core'; export class Bohater { id: number; name: string; } @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>nazwa: </label> <input [(ngModel)]="hero.name" placeholder="name"> </div> ‘ }) export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero: Bohater = {id:12, name:'Jan Paweł II'}; } Wprowadzenie do Angular 2 37/61 6. Dodanie tablicy bohaterów Dodanie tablicy bohaterów i dodanie pola z nimi do klasy: folderApp/app.component.ts .... export class FirstComponent { title = 'Wprowadzenie o bohaterach'; hero: Bohater = {id:12, name:'Jan Paweł II'}; public heroes = BOHATEROWIE; } var BOHATEROWIE: Bohater[] = [ {id:11, name:"Jan III Sobieski"}, {id:12, name:"Jan Paweł II"}, {id:13, name:"Władysław II Jagiełło"}, {id:14, name:"św. Faustyna Kowalska"}, {id:15, name:"Jurand ze Spychowa"}, {id:16, name:"Andrzej Kmicic"}, {id:17, name:"Judyta"}, {id:18, name:"św. Małgorzata Maria Alacoque"} ]; Wprowadzenie do Angular 2 38/61 7. Przygotowanie szablonu dla listy bohaterów Dodajemy nagłówek i listę wypunktowaną - szablon dla listy bohaterów: folderApp/app.component.ts ... @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Moi bohaterowie</h2> <ul> <li> <!-- tutaj będzie lista bohaterów --> </li> </ul> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>nazwa: </label> <input [(ngModel)]="hero.name" placeholder="name"> </div> ‘ }) ... Wprowadzenie do Angular 2 39/61 8. Dodanie pętli dla listy bohaterów - ngFor Dodane pętli - dyrektywa *ngFor: folderApp/app.component.ts ... @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Moi bohaterowie</h2> <ul> <li *ngFor="let hero of heroes"> <span>{{hero.id}}</span> {{hero.name}} </li> </ul> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>nazwa: </label> <input [(ngModel)]="hero.name" placeholder="name"> </div> ‘ }) ... Wprowadzenie do Angular 2 40/61 Wynik działania aplikacji Wprowadzenie do Angular 2 41/61 9. Obsługa wyboru bohatera - click event Dodajemy atrybut (click) z przypisaną mu metodą onSelect() klasy FirstComponent: folderApp/app.component.ts ... <h2>Moi bohaterowie</h2> <ul> <li *ngFor="let hero of heroes" (click)="onSelect(hero)"> <span>{{hero.id}}</span> {{hero.name}} </li> </ul> ... Wprowadzenie do Angular 2 42/61 10. Obsługa wyboru bohatera - dodanie pola i metody Do FirstComponent dodajemy pole selectedHero oraz metodę onSelect(): folderApp/app.component.ts ... export class FirstComponent { title = 'Wprowadzenie o bohaterach'; selectedHero: Bohater; onSelect(hero: Bohater) { this.selectedHero = hero; } // hero: Bohater = {id:12, name:'Jan Paweł II'}; public heroes = BOHATEROWIE; } ... Wprowadzenie do Angular 2 43/61 11. Obsługa wyboru bohatera - poprawki zmiennych i ngIf W szablonie dostosowujemy nazwy do zmian w FirstComponent, dodajemy ngIf: folderApp/app.component.ts @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Moi bohaterowie</h2> <ul class="heroes"> <li *ngFor="let hero of heroes" (click)="onSelect(hero)"> <span>{{hero.id}}</span> {{hero.name}} </li> </ul> <div *ngIf="selectedHero"> <h2>Informacje szczegółowe o {{selectedHero.name}}!</h2> <div><label>id: </label>{{selectedHero.id}}</div> <div> <label>nazwa: </label> <input [(ngModel)]="selectedHero.name" placeholder="name"> </div> </div> ‘ }) Wprowadzenie do Angular 2 44/61 Wynik działania aplikacji - lista po zmianach Wprowadzenie do Angular 2 45/61 Gdzie jesteśmy - obsługa wyboru bohatera Gdzie jesteśmy? Mamy tylko jeden komponent - FirstComponent zawierający: tytuł naszej aplikacji, wybranego bohatera, metodę do wyboru bohatera, listę bohaterów, tablica z bohaterami znajduje się w tym samym pliku, mamy też klasę odpowiedzialną za pojedynczego bohatera - również w tym samym pliku, Funkcjonalność: wyświetlanie listy bohaterów, możliwć edycji wybranego bohatera. Wprowadzenie do Angular 2 46/61 12. Osobny komponent do wyświetlania szczegółów Przechowywanie w jednym komponencie obsługi listy bohaterów i wyświetlania szczegółów o nich narusza zasadę pojedynczej odpowiedzialności. Dzielimy tę funkcjonalność na dwa osobne komponenty. Tworzymy komponent HeroDetailComponent, wersja początkowa: folderApp/hero-detail.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'my-hero-detail', }) export class HeroDetailComponent { } Wprowadzenie do Angular 2 47/61 13. Osobny komponent z szablonem Do komponentu HeroDetailComponent przenosimy kod szablonu odpowiadający za szczegóły wybranego bohatera: folderApp/hero-detail.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'my-hero-detail', template: ‘ <div *ngIf="hero"> <h2>Informacje szczegółowe o {{hero.name}}!</h2> <div><label>id: </label>{{hero.id}}</div> <div> <label>nazwa: </label> <input [(ngModel)]="hero.name" placeholder="name"> </div> </div> ‘ }) export class HeroDetailComponent { } Zmiana selectedHero na hero. Wprowadzenie do Angular 2 48/61 14. Klasa Bohater w osobnym pliku Przenosimy klasę Bohater do osobnego pliku - będzie potrzebna w obu komponentach: folderApp/bohater.ts export class Bohater { id: number; name: string; } W obu komponentach dodajemy import utworzonej klasy: import { Bohater } from './bohater'; Do klasy HeroDetailComponent dodajemy pole hero (@Input): folderApp/hero-detail.component.ts ... export class HeroDetailComponent { @Input() hero: Bohater; } Wprowadzenie do Angular 2 49/61 Pole hero jako Input Komponent HeroDetailComponent musi wiedzieć, którego bohatera dane ma wyświetlać. Informacje o tym posiada komponent nadrzędny: FirstComponent. Chcemy wyświetlić szczegóły wybranego bohatera. Komponent nadrzędny wiąże pole selectedHero z polem hero komponentu podrzędnego: Pole docelowe, w nawiasach [ ] po lewej stronie znaku = musi być wejściowym (input): folderApp/app.component.ts (fragment szablonu) ... <my-hero-detail [hero]="selectedHero"></my-hero-detail> ... Wprowadzenie do Angular 2 50/61 15. FirstComponent po zmianach FirstComponent z importem drugiego komponentu i zmienionym szablonem zawierającym metadane o nowym komponencie: folderApp/app.component.ts (fragment) import { Component } from '@angular/core'; import { HeroDetailComponent } from './hero-detail.component'; import { Bohater } from './bohater'; @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Moi bohaterowie</h2> <ul class="heroes"> <li *ngFor="let hero of heroes" (click)="onSelect(hero)"> <span>{{hero.id}}</span> {{hero.name}} </li> </ul> <my-hero-detail [hero]="selectedHero"></my-hero-detail> ‘, directives: [HeroDetailComponent] }) ... Wprowadzenie do Angular 2 51/61 Tablica directives Przeglądarka ignoruje nieznane znaczniki HTML - tak samo Angular. Sam import komponentu HeroDetailComponent nie wystarczy. Należy jeszcze poinformować o tym Angular! Czynimy to dodając nazwę komponentu do tablicy directives. folderApp/app.component.ts (konfigurowanie kompoentu) @Component({ selector: 'my-app', template: ‘ <h1>{{title}}</h1> <h2>Moi bohaterowie</h2> <ul class="heroes"> <li *ngFor="let hero of heroes" (click)="onSelect(hero)"> <span>{{hero.id}}</span> {{hero.name}} </li> </ul> <my-hero-detail [hero]="selectedHero"></my-hero-detail> ‘, directives: [HeroDetailComponent] }) ... Wprowadzenie do Angular 2 52/61 15. FirstComponent po zmianach II Klasa FirstComponent jako nadrzędny komponent: folderApp/app.component.ts (fragment) ... export class FirstComponent { title = 'Wprowadzenie o bohaterach'; selectedHero: Bohater; onSelect(hero: Bohater) { this.selectedHero = hero; } public heroes = BOHATEROWIE; } var BOHATEROWIE: Bohater[] = [ {id:11, name:"Jan III Sobieski"}, {id:12, name:"Jan Paweł II"}, {id:13, name:"Władysław II Jagiełło"}, {id:14, name:"św. Faustyna Kowalska"}, {id:15, name:"Jurand ze Spychowa"}, {id:16, name:"Andrzej Kmicic"}, {id:17, name:"Judyta"}, {id:18, name:"św. Małgorzata Maria Alacoque"} ]; Wprowadzenie do Angular 2 53/61 16. Tworzenie usługi Nazwa pliku usługi kończy się na *.service.ts - taka konwencja. Eksportujemy klasę HeroService - usługa z listą bohaterów. Importujemy funkcję Injectable i stosujemy ją korzystając z wzorca dekorator. Dzięki temu Angular śledzi zależności tej usługi od innych - emituje metadane o naszej usłudze. Nawet, jeśli nasza usługa nie ma żadnych zależności, dobrze jest od początku dodawać dekorator @Injectable(). folderApp/bohater.service.ts import { Injectable } from '@angular/core'; @Injectable() export class HeroService { } Wprowadzenie do Angular 2 54/61 17. Imitowane dane naszych bohaterów Przenosimy dane z pliku app.component.ts do osobnego pliku, specjalnie dla nich. folderApp/dane-bohaterowie.ts import { Bohater } from './bohater'; export var BOHATEROWIE: Bohater[] = [ {id:11, name:"Jan III Sobieski"}, {id:12, name:"Jan Paweł II"}, {id:13, name:"Władysław II Jagiełło"}, {id:14, name:"św. Faustyna Kowalska"}, {id:15, name:"Jurand ze Spychowa"}, {id:16, name:"Andrzej Kmicic"}, {id:17, name:"Judyta"}, {id:18, name:"św. Małgorzata Maria Alacoque"} ]; W pliku app.component.ts pole z listą bohaterów zostawiamy puste: folderApp/app.component.ts heroes: Bohater[]; Wprowadzenie do Angular 2 55/61 18. Metoda dostępowa Użytkownik usługi nie wie z jakiego źródła są dane. Dane mogą pochodzić z Web Serwisu, z lokalnego pliku albo być imitowane. To jest piękno korzystania z usług! Usługa odpowiada za dostęp do danych. W każdej chwili można zmienić sposób dostępu - zmiany są tylko w tej jednej usłudze. folderApp/bohater.service.ts import { Injectable } from '@angular/core'; import { BOHATEROWIE } from './dane-bohaterowie'; @Injectable() export class HeroService { getHeroes() { // return BOHATEROWIE; return Promise.resolve(BOHATEROWIE); } } Wprowadzenie do Angular 2 56/61 Wykorzystanie usługi Importujemy potrzebną nam usługę - możemy się do niej odwołać w kodzie. Jak Angular utworzy tę usługę? Nie tworzymy jej samodzielnie, nie korzystamy z new ponieważ: nasz komponent musiał by wiedzieć jak utworzyc usługę; zmiana konstruktora usługi wymagała by zmiany jego wywołania w wielu miejscach... samodzielne tworzenie usługi w każdym komponencie utowrzy wiele jej instancji ... co jeśli będzie potrzebna tylko jedna współdzielona? wiąże nas to z konkretną implementacją HeroService - ciężko przejść na inne scenariusze, zmieniać dane do testowania... Dlatego też stosujemy wstrzykiwanie zależności. Wprowadzenie do Angular 2 57/61 19. Wykorzystanie usługi folderApp/app.component.ts ... import { HeroService } from './bohater.service'; import { OnInit } from '@angular/core'; @Component({ ... providers: [HeroService] }) export class AppComponent implements OnInit { constructor(private heroService: HeroService) { } ngOnInit() { this.getHeroes(); } title = 'Wprowadzenie o bohaterach'; selectedHero: Bohater; onSelect(hero: Bohater) { this.selectedHero = hero; } heroes: Bohater[]; // public heroes = BOHATEROWIE; getHeroes() { // this.heroes = this.heroService.getHeroes(); this.heroService.getHeroes().then(heroes => this.heroes = heroes); } } Wprowadzenie do Angular 2 58/61 Wykorzystanie usługi Stosujemy wstrzykiwanie zależności. Zamiast wykorzystania operatora new do komponentu dodamy: konstruktor z parametrem potrzebnej usługi: parametr tworzy automatycznie prywatne pole heroService, pole heroService zostanie rozpoznane jako klasa HeroService, która jest tworzona przez wstrzykiwanie zależności, Angular będzie wiedział, aby dostarczyć instancję usługi, przy tworzeniu komponentu, metadane z listą dostawców (providers) potrzebnych usług: informują Injector jak utworzyć usługę HeroService, usługę należy dodać do listy dostawców (providers) w metadanych konfigurujących komponent, tablica providers informuje Angular, że ma utworzyć nową instancję usługi przy tworzeniu komponentu. Wprowadzenie do Angular 2 59/61 Wstawki programowe - ngOnInit Pobieranie danych z usługi powinno się odbywać automatycznie. Gdzie dodać kod odczytu danych z usługi? Nie dodajemy logiki do konstruktora, szczególnie takiej, która łączy się z serwerem. Wczytywanie danych powinno odbywać się już na utworzonym komponencie. Wykorzystamy wstawki programowe dla cyklu życia komponentu ngOnInit. Angular sam wywoła odpowiedną metodę przy określonym stanie komponentu. Wprowadzenie do Angular 2 60/61 Źródła https://angular.io/ https://pl.wikipedia.org/wiki/TypeScript https://www.typescriptlang.org/ http://codeguru.geekclub.pl/baza-wiedzy/ wprowadzenie-do-programowania-w-typescript-wstep,3575 https://developer.mozilla.org/en-US/docs/Web/JavaScript/ Reference/Functions/Arrow_functions Wprowadzenie do Angular 2 61/61