We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Angular 2: Getting Started

00:00

Formal Metadata

Title
Angular 2: Getting Started
Title of Series
Number of Parts
84
Author
License
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Anhand einer Demo-Anwendung wird gezeigt wie Angular 2 aufgebaut ist und wie man loslegen kann.
16
Thumbnail
41:34
27
Thumbnail
08:57
36
Thumbnail
48:55
56
58
Thumbnail
1:02:53
67
Thumbnail
45:41
68
69
Thumbnail
37:44
Slide ruleComputer programmingDownloadHand fanSet (mathematics)Video game consoleRun-time systemWeb browserJavaScriptTuningFocus (optics)Computer fileCodeWEBXMLUMLComputer animationLecture/Conference
Video game consoleCodeSeries (mathematics)Computer fileLaufzeitComputer animation
Server (computing)Computer fileCompilerLaufzeitHTMLMoment (mathematics)AbstractionComponent-based software engineeringParallelenParameter (computer programming)Social classSelektorWeb browserUniform resource locatorTemplate (C++)Video game consoleSource codeMobile appError messageConfiguration spaceMechanism designModule (mathematics)Module (mathematics)Route of administrationInstanz <Informatik>Set (mathematics)Computing platformIndexFunction (mathematics)JavaScriptContent (media)EckeVariable (mathematics)Computer animation
Instanz <Informatik>Component-based software engineeringSocial classPlane (geometry)Mathematical structureComputer animation
Source codeComponent-based software engineeringVariable (mathematics)Mobile appEquals signTemplate (C++)Social classLogical constantListe <Informatik>Data modeloutputSelektorKommunikationComputer fileModule (mathematics)Source codeComputer animation
Component-based software engineeringSocial classInformationTemplate (C++)Mechanism designoutputVersion <Informatik>TOMZugriffiPodVariable (mathematics)Route of administrationInternetNormaleComputer animation
Template (C++)Web serviceSocial classCursor (computers)outputVideo game consoleComponent-based software engineeringMobile appListe <Informatik>PredictionComputer animation
Web serviceComponent-based software engineeringSocial classVortexDatabaseMISSKommunikationConstructor (object-oriented programming)Parameter (computer programming)Software testingProviderVideo game consoleManual of StyleTape driveInterface (computing)ImplementationDatenerhebungEncapsulation (object-oriented programming)TelecommunicationSequelInterface (computing)LengthACCESS <Programm>Software testabilityHTTPLogical constantData storage deviceMobile appData modelComputer animation
Plane (geometry)Mechanism designProviderComponent-based software engineeringSocial classWeb serviceInstanz <Informatik>Series (mathematics)Video game consoleSoftware testingSingle-precision floating-point formatKommunikationFactory (trading post)Generating functionMobile appComputer animationLecture/Conference
Web serviceComponent-based software engineeringKommunikationLanglebigkeitListe <Informatik>Boom (sailing)Social classLecture/ConferenceComputer animation
CompilerError messageCompilerComputer fileTemplate (C++)Computer animationSource code
KommunikationCache (computing)Component-based software engineeringMobile appServer (computing)Computer animationLecture/Conference
Component-based software engineeringDatabaseTemplate (C++)Object (grammar)Texture mappingString (computer science)TestdatenData modelHTTPCodeProviderConstructor (object-oriented programming)Logical constantWeb browserMechanism designWeb serviceServer (computing)ImplementationComputer animationLecture/Conference
Web serviceServer (computing)KommunikationJavaScriptUniform resource locatorWeb browserDisk read-and-write headProxy serverHTTPPILOT <Programmiersprache>Computer fileComputer animation
Web browserServer (computing)APIProviderRoutingMobile appRouter (computing)Moment (mathematics)Component-based software engineeringObject (grammar)Computer fileUniform resource locatorWeb serviceLevel (video gaming)CodeMechanism designFile viewerParameter (computer programming)Configuration spaceWalkthroughSpring (hydrology)TowerComputer animation
Mobile appCodeRouter (computing)Link (knot theory)Wind waveComputer animation
Constructor (object-oriented programming)CodeComputer fileImplementationWeb serviceHierarchyMoment (mathematics)Component-based software engineeringFocus (optics)Mechanism designString (computer science)Mobile appMathematical structureComputer animation
Web serviceNavigationLink (knot theory)Software testingComputer fileHighlight <Programm>Router (computing)HTMLHash functionComputer animation
EmailIP addressTwitterLecture/ConferenceComputer animation
Transcript: German(auto-generated)
Hallo, herzlich willkommen auch an meiner Seite zur Angular 2 Live-Demo. Ganz kurz zu meiner Person. Ich habe nicht viele Slides, es sind vier oder fünf Slides. Danach machen wir nur noch in der IDE. Mein Name ist Philipp Burgmar.
Ich komme von WFK aus Esslingen. Ich mache Softwareentwicklung. Bin Trainer hauptsächlich im Web-Umfeld, Web-Technologie, ein bisschen Security. Web Security mache ich auch, allerdings nicht Hauptschwerpunkt. In letzter Zeit beschäftige ich mich hauptsächlich mit TypeScript und Angular.
Wir bieten da Schulungen an in dem Bereich Projekt-Kickoffs und Unterstützung im Projekt. Seit 2007 schon, seit 2013 unter diesem Label The Code Campus. Das soll uns aber gar nicht groß interessieren, sondern wir wollen Angular 2 heute angucken. Wir wollen gucken, wie man ein Projekt anlegt, wie man so ein bisschen loslegt.
Und dafür auch direkt die Slides dann verlassen und nur noch in der IDE und im Browser uns aufhalten. Wer hat schon was mit Angular 2 gemacht? Noch keiner, okay. Ach doch, da einer. Wer hat mit Angular 1 was gemacht? Ein paar, gut. Mit TypeScript schon mal.
Okay, aber JavaScript, wer hat keine JavaScript-Kenntnisse? Keiner, okay, das ist schon mal gut. Wir legen ganz von vorne los. Ich möchte euch jetzt nicht in der nächsten Stunde irgendwie zeigen, was man mit Angular 2 alles für Fancy-Sachen machen kann, sondern ich möchte euch zeigen und den Fokus darauf legen, was man wirklich so machen würde, wenn man jetzt anfängt,
was so die ersten Schritte sind und wie man da vorgeht. Die Sachen, die ich zeige, die Beispiele sind alle bei uns aus dem Trainingsunterlagen raus. Da machen wir das im Prinzip auch genau in dem Stil, nur dass ich es halt sehr zusammen shrink, dass wir in einer Stunde möglichst viel angucken können.
Ich gehe auf die Konsole. Wenn man irgendwas nicht lesen kann oder ich zu schnell oder zu langsam bin oder so, dann sagt es bitte. Wenn Fragen sind, haben wir abgesprochen, dass ich die Frage dann einfach nochmal wiederhole. Und ihr könnt also zwischendrin ruhig gerne Fragen stellen. Um mit Angular loslegen zu können, brauchen wir ein bisschen Toolink.
Wir wollen mit TypeScript entwickeln. TypeScript kann der Browser nicht nativ ausführen. Wir brauchen also ein Cross-Compiling. Für TypeScript gibt es keine Laufzeitumgebung. Deswegen brauchen wir irgendwie Tooling, das uns unseren TypeScript-Code in JavaScript-Code übersetzt und der dann vom Browser ausgeführt werden kann. Dafür gibt es zum Glück jetzt von einem Angular-Team selber ein Tool, das sich Angular CLI nennt.
Das kann man über NPM installieren. Wir machen also ein NPM install angular-cli-g. Globale Installation, damit wir es danach im Pfad verfügbar haben, habe ich schon gemacht. Brauche ich jetzt nicht machen. Danach haben wir das Kommando ng, mit dem wir diverse Sachen ausführen können.
Und was wir als erstes machen, ist erstmal in ein leeres Verzeichnis gehen. Ich gehe einfach mal bei mir in Downloads. Und da sagen wir jetzt einfach mit ng new. Ich nenne es jetzt den TCC Manager App, weil wir so eine kleine Schulungsverwaltungssoftware machen wollen.
Wenn wir das durchlaufen lassen, dann generiert er uns ein neues Verzeichnis. In dem Verzeichnis packt er ein paar Dateien rein und führt danach ein NPM install aus. Ich habe vorhin mal getestet, das Netz scheint hier recht gut zu sein. Deswegen sollte das jetzt nicht allzu lange dauern. Derweil gehe ich hier in meine IDE. Ich verwende jetzt Visual Studio Code.
Man kann genauso gut auch irgendeine andere IDE mit vernünftiger TypeScript und Web-Unterstützung verwenden. Bei der Arbeit verwende ich am liebsten WebStorm bzw. IntelliJ. Aber Visual Studio Code reicht eigentlich auch komplett aus. Ich gehe zu dem Ordner, habe hier diesen Ordner, den ich gerade angelegt habe, und öffne den Ordner.
Er hat uns eine ganze Reihe von Dateien generiert, ein komplettes Projekt-Setup. Er hat hier Dependencies eingetragen in der Package-JSON, was wir an Laufzeitabhängigkeiten haben. Hier sind die ganzen Angular-2-Module drin. Er hat hier unten das Angular-CLI auch nochmal lokal installiert, sodass wir uns nicht von einer globalen Installation abhängig machen.
Diese globale Installation, die ich gerade gezeigt habe, ist wirklich nur damit wir auf der Konsole dieses Commando-NG haben. Und die delegiert dann immer an die lokal installierte. Wir haben Test-Support und Linting und sowas alles direkt mit drin. Und mit ein bisschen Glück ist es jetzt hier durchgelaufen.
Und wir können danach, wenn wir in dieses Verzeichnis reingehen, TCC-Manager-App mit ng-serve, einen Development-Server starten, der im Hintergrund dann die Dateien watcht, den TypeScript-Compiler ausführt und eben einen kleinen lokalen Server startet, sodass ich unter localhost-4200 dann die Anwendung aufrufen kann.
Da ist meine Anwendung, die jetzt noch nichts groß tut, außer dass sie anzeigt, dass die Anwendung geht. Das ist das, was das Angular-CLI uns rausgeneriert. Beim Entwickeln bietet es sich immer an, die Developer-Tools offen zu haben und dort die Konsole zu öffnen. Hier tauchen halt die Fehlermeldungen von Angular auf.
Das heißt, wir haben so ein bisschen zweischichtig, wie wir es halt in der Webentwicklung haben. Wir haben auf der einen Seite auf der Konsole bzw. der IDE die Sprache an sich, also den TypeScript-Compiler, der uns da Meldungen anzeigt und dann zur Laufzeit halt die Exceptions in der Browser-Konsole. Man kann sich das Ganze auch noch einrichten, dass man sowas in der IDE angezeigt kriegt,
aber das würde jetzt ein bisschen zu weit führen und ist auch relativ aufwendig. Das heißt, unsere Anwendung läuft. Wir gehen einfach mal kurz her und gucken, was wir denn da so rausgeneriert bekommen haben. Wir haben unter Source eine Index-HTML. In der Index-HTML sind im Prinzip zwei Sachen wichtig.
Wir haben hier unten einen System-Import. Angular 2 setzt sehr stark auf das Modulsystem von ECMAScript 6 oder auch das, was in TypeScript eben unterstützt wird. Und hier ist jetzt eben eingestellt, dass wir System.js als Module-Loader-Polyfill verwenden, um diese Module zur Laufzeit laden zu können.
Das ist ja so ein bisschen die Problematik im Moment in der JavaScript-Welt, dass wir zwar eine Modulspezifikation haben, wie wir Import- und Exportstatement schreiben, aber es noch nicht wirklich so einen richtig standardisierten und breit unterstützten Support dafür gibt, diese Module oder diese Syntax dann halt zur Laufzeit auch wirklich zu laden. Deswegen haben wir hier ein Polyfill eingebunden und das stellt diese globale variable System zur Verfügung.
Wir importieren uns Konfiguration für dieses System.js und danach starten wir unsere Anwendung, indem wir dieses Main-Modul importieren. Dieses Main-Modul ist hier nebendran diese Main.ts-Datei. Und da taucht dann eigentlich die erste Angular-spezifische Sache auf, nämlich dass wir hier oben aus dem Angular-Paket, das Angular selber ist,
in mehrere kleinere Pakete aufgeteilt und da wird so ein Scoping gemacht. Das liegt alles unter Edge Angular und darunter gibt es dann Subpakete und wir importieren hier Platform Browser Dynamic. Dynamic heißt deshalb, weil wir das Ganze kompilieren und Templating und sowas im Browser machen. Es wird später auch noch einen Mechanismus geben, dass man das zur Buildzeit machen kann.
Dann würde man hier eben nicht aus Browser Dynamic importieren. Im Gegensatz zu Angular 1 müssen wir eine Angular 2-Anwendung immer explizit von Hand bootstrappen. Das heißt, es gibt kein ng-App, das wir irgendwo dran schreiben und dann tut es einfach. Sondern wir müssen explizit diese Bootstrap-Funktion importieren und dann aufrufen. Das machen wir hier unten.
Da werden wir nachher noch ein paar Mal darauf zurückkommen, dass wir hier hintendran noch einen zweiten Parameter mitgeben, um das weiter zu konfigurieren. Das erste, was wir mitgeben, ist die App-Component. Das ist die Einstiegskomponente. Eine Angular-Anwendung besteht immer aus einem Baum von Komponenten. Die können ineinander dann eben verschachtelt sein, sodass es sich ein Baum ergibt.
Ihr könnt euch das vorstellen, wie der DOM oder das HTML an sich auch. Das ist auch eine Baumstruktur. Wir machen jetzt quasi eine Abstraktionsebene oben drüber, eine Baumstruktur von Komponenten. Wir sagen Angular hier, das ist die Root-Komponente, der Rootknoten von diesem Baum. Damit soll unsere Anwendung initialisiert werden.
Wir haben es hier oben aus dem App-Ordner importiert. Parallel zu dieser Main.ts in App liegt unsere Komponente. Dort können wir hinspringen und sehen dann den groben Aufbau einer Komponente in Angular 2. Eine Komponente ist eine Klasse. Diese Klasse ist annotiert mit dieser Add-Component-Annotation.
In TypeScript heißt es, wenn man es korrekt sagt, Decorator, nicht Annotation. Und wir haben hier drin drei wichtige Sachen. Dieses Module-ID oben kann uns erstmal egal sein. Wir haben hier den Selektor. Und dieses Selektor sagt, wann diese Komponente aktiv werden soll. Das ist das Restrict quasi in Kombination mit dem direktiven Namen aus Angular 1.
Wir können hier einen CSS-Selektor angeben. Wenn wir hier einfach nur App-Root hinschreiben, dann wäre es nach CSS-Regeln ja ein Elementselektor. Und wenn wir jetzt in die Index-HTML zurückgehen, dann sehen wir, dass hier oben genau dieses App-Root steht. Und diese Kombination ist wichtig, dass dieser Aufruf der Bootstrap-Funktion,
wo wir diese annotierte Klasse reinreichen, und in der Annotation steht dieses App-Root drin, sagt eben, dass in der Index-HTML das hier gesucht werden soll und da dann das Template der Komponente eingesetzt werden soll. Das heißt, ich gehe nochmal zurück zu der Main.ts. Das heißt, wir importieren hier diese App-Komponente, überreichen diese annotierte Klasse.
Das ist nicht die Instanz, sondern das ist wirklich die Referenz auf die Klasse. In diese Bootstrap-Funktion an der App-Komponent steht diese Add-Komponent-Annotation dran. Und da steht der Selektor drin. Wir haben eine Template URL angegeben. Die liegt hier parallel. Deswegen brauchen wir hier oben auch dieses Module-ID, damit dieser relative Name aufgelöst werden kann.
Und wir haben eine Style-Datei. Hier wird CSS verwendet. Man kann auch Less oder Sass oder Stylus verwenden. Wir haben in der App-Komponente einfach diese Überschrift, die mit Data-Binding arbeitet. Und dieser Wert kommt hier aus dieser Property, die in der Instanz von dieser Klasse,
die von Angular erzeugt wird, dann drin steht. Deswegen taucht im UI, wenn ich hier nochmal hingehe, hier dieser Text hier oben auf. Was wir jetzt machen wollen, ist eine klassische Master-Detail-Ansicht. Wir wollen oben eine Liste von Trainings anzeigen, die wir anbieten. Und unten drunter eine Detail-Ansicht. Wir wollen oben eins anklicken können und es wird unten angezeigt.
Dafür brauchen wir verschiedene Komponenten. Ich muss mir als erstes einen Ordner anlegen, um das CLE ein bisschen zu unterstützen. Das kann leider keine tiefer geschachtelten Strukturen. Deswegen mache ich mir einen Feature-Ordner namens Trainings. Und jetzt kann ich ng-generate verwenden.
Das CLE hat nicht nur dieses Scaffolding mit dabei, dass wir ein neues Projekt anlegen können, sondern wir können auch während wir im Projekt arbeiten uns immer wieder Snippets generieren lassen. Wir sagen dann, dass wir eine Komponente haben wollen und wie die heißen soll und wo sie hin soll. Trainings slash Training-List.
Jetzt wird mir, das habe ich doch gerade angelegt oder habe ich mich da oben vertippt? mk-dea-trainings-file-exist.
Ah, danke. Ich muss natürlich noch in source-app rein und mache da meinen. Und dann gehe ich raus bzw. ich könnte es wahrscheinlich auch auf der Ebene. Und da sage ich dann noch mal mein ng-generate. Das gleiche, was ich jetzt für die Liste gemacht habe, mache ich auch für die Details.
Und habe dann zwei Komponenten angelegt. Also ich habe nur den Quellcode dafür generieren lassen. Wir haben jetzt hier unter Trainings einen Unterordner mit Details und List. Und was wir als erstes machen wollen ist, dass wir in die App-Component gehen und diese beiden Sachen auch verwenden. Ich schmeiße diesen Titel hier raus und stattdessen schreiben wir hier einfach den hard codierten Namen hin.
Und wenn wir jetzt in unsere Training-List-Datei reingucken, sehen wir hier oben wieder diesen Selector. Das muss ich angeben, damit diese Komponente nachher gefunden wird. Wenn ich zurück in die HTML-Datei gehe, kann ich mir das hier anlegen.
List soll das noch heißen. Und genauso haben wir die App Training Details. Wenn man dieses App vorne dran nicht mag, was bei mir der Fall ist, ich finde es ein bisschen blöd, dass es immer mit App-Prefixes ist, kann man in der CLI-JSON konfigurieren, dass das nicht gemacht wird. An dieses Training-List wollen wir jetzt Daten übergeben.
Unsere App-Komponente stellt gleich Daten zur Verfügung, irgendwelche MOC-Daten. Und die wollen wir jetzt an die Liste übergeben. Wir wollen möglichst gekapselte Komponenten machen, die sich einfach von Außendaten reinreichen lassen. Dafür können wir mit sogenannten Property Bindings arbeiten. Dafür gehen wir als erstes mal in die Liste rein und sagen, dass es hier ein Feld Trainings geben soll.
Und wir arbeiten mit TypeScript und wollen jetzt hier auch einen Typ angeben. Wir wollen jetzt hier sagen, das ist ein Trainings-Array. Und da wollen wir auf der einen Seite dann sagen, dass das von außen reingereicht wird.
Dafür gibt es die AddInput-Annotation. Die können wir hier oben aus dem Angular-Core-Modul importieren. Da gibt es die Input-Annotation. Jetzt fehlt uns noch diese Trainings-Klasse. Die können wir jetzt anlegen. Ich kopiere mir die mal eben.
Wir haben hier eine Model-Klasse. Warum gibt es jetzt kein Paste? Machen wir es im Explorer.
Wir haben hier unseren Source App Trainings. Und hier haben wir unsere Training-Model und auch die MOC. Das kopiere ich mal eben darüber. Wir haben jetzt hier unter Trainings unsere Model-Klasse.
Das ist einfach nur eine Klasse, die ein paar Felder definiert. Und in unserer MOC-Datei haben wir jetzt ein paar Instanzierungen von dieser Klasse, damit wir hier unter diesem Trainings und der Konstante einfach ein paar MOC-Daten haben. Werden wir nachher noch ersetzen und gegen Backend-Kommunikation austauschen. Wir können jetzt hier hergehen und hier oben eben Import.
Eigentlich bietet die IDE auch automatische Imports an. Das funktioniert aber leider noch nicht ganz so zuverlässig. Wir sind in der Training-List-Komponente. Das heißt, wir müssen eins raus und haben dann Training.Model. Und von dort können wir die Klasse Training importieren.
Und die wollen wir jetzt natürlich zuweisen. Das heißt, über dieses Input sagen wir, dass die Daten bitte von außen reingereicht werden sollen. Diese Komponente beschafft sich die Daten nicht selber, sondern sie definiert nach außen eine Dependency, dass sie diese Daten gerne reingereicht haben möchte. Das heißt, wir müssen hier an der Stelle, wo wir diese Komponente verwenden, diese Daten reinreichen.
Und das macht man über ein sogenanntes Property-Binding. Man schreibt eine eckige Klammer, damit hier wirklich ein Binding gemacht wird, ein Synchronisieren. Und hier kommt jetzt das Target rein. In welche Property sollen denn die Daten jetzt reingesteckt werden? Dazu muss ich jetzt wieder hier gucken, wie ich es genannt habe. Der Name hier ist entscheidend. Trainings. Der muss da hin. Und hier außen hinter dem Gleichheitszeichen gebe ich jetzt an, wo die Daten herkommen.
Ich nenne es jetzt bei mir einfach mal List, damit die Namen sich ein bisschen unterscheiden. Dieses List ist jetzt eine Property in der Komponente, in der ich mich gerade befinde. Das heißt, ich bin in der App Component, in dem Template dazu. Also muss ich in der App Component TS hier statt Title, was ich ja im Binding gelöscht habe,
eine Property List zur Verfügung stellen. Die soll natürlich auch wieder von Typ Training Array sein. Und da möchte ich jetzt gerne irgendwelche Daten haben. Und dafür benutze ich jetzt meine Konstante erstmal aus den Mockdaten. Das heißt, ich gehe hier wieder her, importiere aus Slash Trainings Slash Training Punkt Mock.
Und von dort die Trainings. Die Klasse muss ich auch noch importieren. Und die kommt aus Model.
Das heißt, wenn wir diese Listen Komponente jetzt hier eingebunden haben und ihr Daten übergeben, gehen wir als nächstes in das Template von dieser Listen Komponente. Wir wollen hier nicht irgendwelchen Text anzeigen, sondern wir wollen jetzt hier eine Liste anzeigen, mit einem Eintrag, bei dem wir für das einzelne Training immer den Namen ausgeben.
Jetzt haben wir eine Liste von Trainings und wollen natürlich dieses Li wiederholen. Dafür gibt es die ng-for-Direktive. Das ist der ng-repeater quasi von Angular 2. Und hier schreiben wir von der Syntax genauso was, wie wir in einer ECMAScript 6 for off Schleife schreiben würden.
Das heißt, wir sagen mit let, dass wir eine lokale Variable innerhalb der Schleife haben wollen. Ich habe es jetzt hinten schon Training genannt. Das heißt, hier schreibe ich jetzt auch mal Training und dann off. Und wo kommt es her? Es kommt aus dem Feld Trainings. Dieses Trainings hier hinten ist jetzt wieder dieses Trainingsfeld hier in der Komponenten Klasse.
Das heißt, wenn das jetzt funktioniert, dann würden wir jetzt hier unten eine Liste angezeigt kriegen. Tun wir aber nicht, denn wir haben noch eins vergessen. Wir müssen diese Komponente bekannt machen. In dem derzeitigen Stand von Angular ist es so, dass wir Komponenten immer explizit bekannt machen müssen. Das heißt, wenn wir in der App Komponente hier unten in dem Template diese Training List und Training Details Komponente verwenden,
dann müssen wir sie hier in der App Komponente in der Klasse auch bekannt machen. Das heißt, wir gehen hier her und sagen hier über dieses Directives Feld, dort geben wir einen Array rein. Und hier müssen wir jetzt Training List Komponent und Training Details Komponent reinreichen.
Und die müssen wir natürlich dann oben wieder als Klasse importieren. Da wird es in der nächsten Version auch nochmal einen etwas anderen Mechanismus geben, also im nächsten Release Candidate. Da wird es sogenannte ng-Modules geben, um das Ganze in größeren Anwendungen ein bisschen besser modularisieren zu können.
Wir gehen in unseren Trainings Ordner dort in Training List und importieren von dort die Training List Komponent. Und das Gleiche machen wir natürlich auch für die Details.
Und wenn wir jetzt reingehen, dann sehen wir, dass die beiden Komponenten gefunden wurden. Oben wird die Liste ausgegeben mit den Mockdaten. Unten wird einfach noch diese statische Template ausgegeben von der Detail Komponente. Da habe ich ja noch gar nichts gemacht. Was ich jetzt als nächstes machen möchte, ist eben anbieten, dass man oben in der Liste eins anklicken kann
und die entsprechenden Details unten angezeigt werden. Dafür muss ich erstmal irgendwie ein Click Event entgegennehmen. Und wenn ich dieses Click Event haben möchte, dann kann ich mit den Event Bindings arbeiten.
Das ist ein vereinheitlicher Mechanismus. Wir brauchen keine eigenen Direktiven für ng-Click, ng-Mouseover und sowas mehr, sondern wir schreiben für ein Event immer eine runde Klammer. Und dazwischen kommt der Event Name. Wenn ich jetzt das ganz normale DOM Event haben möchte, das hier dran geschmissen wird, dann sage ich, okay, ich möchte das Click Event haben und da möchte ich jetzt irgendwie eine Methode aufrufen.
Ich sage, die heißt onListItemClicked. Und da ich in meiner Controller-Klasse, in der Komponenten-Klasse nur die Information habe, ich stelle eine Liste da, weiß ich gar nicht, um welches einzelne Training es geht. Also reiche ich hier noch das Training rein. Ich habe hier in dieser Expression, die ich hier angebe, Zugriff auf diese lokale Variable von der Schleife.
Das muss ich natürlich implementieren. Ich gehe in meine Komponente und sage hier unten onListItemClicked. Und dort kommt ein Training rein vom Typ Training.
Und da drin wollen wir jetzt irgendetwas machen. Und was wir da drin machen, ist quasi eine Aggregation. Die Komponente weiß, wie ihr Template aufgebaut ist und wie ein Training selektiert wird. Vielleicht wäre auch ein Button hinten dran oder so etwas. Dieses DOM Event möchten wir aber nicht nach außen geben, sondern wir wollen nach außen nur die Information geben,
hey, dieses Training wurde jetzt selektiert. Ganz egal, wie das intern im Template abgebildet ist. Das heißt, wir definieren, so wie wir hier oben an den Input definiert haben, jetzt an unserer Komponente auch einen Output. Und dieser Output heißt dann TrainingSelected zum Beispiel. Und dieser Output ist dann vom Typ EventEmitter.
Und hier werden Generics verwendet. Und wir sagen, wir emitten ein Training. Das ist sicherlich nicht der allerschönste Stil. Normalerweise würde man eine extra Eventklasse machen und dann ein TrainingSelected Event oder sowas feuern. Wir machen es uns jetzt erstmal einfach und sagen hier oben,
dass wir Output und EventEmitter importieren. Und wenn wir jetzt hier ein Feld haben, SelectedTraining, und das einen EventEmitter enthält, dann können wir jetzt hier unten sagen, this.TrainingSelectedEmit.
Und dort können wir das selektierte Training reinreichen. Und jetzt kommt das Schöne an der Template Syntax von Angular 2. Der Mechanismus ist genau der gleiche wie bei den DOM Events auch. Ich gehe einfach in die Komponente, in der meine TrainingList verwendet wird. Das ist meine AppKomponente. Hier steht meine TrainingList.
Und so wie ich jetzt hier die TrainingList verwendet habe und Daten reinreiche, kann ich jetzt eben hier auch sagen, naja, dort kommt ja ein TrainingSelectedEvent raus. Und dann gebe ich jetzt an, was soll denn jetzt hier außen in meiner AppKomponente passieren, wenn intern dieses
TrainingSelectedEvent geschmissen wurde. Und dort könnte ich jetzt sagen, ich mache mir ein Feld mit selectedData, nenne ich es einfach mal. Und hier gibt es jetzt immer eine Variable namens dollarEvent, über die ich auf das Event zugreifen kann, das innen drin geschmissen wurde. Wenn ich nochmal zurück zu der Liste gehe, hier habe ich dieses Event nicht verwendet.
Ich könnte jetzt hier genauso gut noch sagen, Komma dollarEvent, dann würde ich tatsächlich Zugriff auf dieses DOM Event kriegen. Das heißt, das ist immer das, was derjenige innen drin als Event losschickt. Wenn ich zurück hier hingehe, kriege ich hier also unter dollarEvent das Trainingsobjekt, das rausgeschickt wird.
Und ich mache mir jetzt hier einfach noch eine selectedDataProperty in dieser AppKomponente. Und sage hier unten selectedData ist vom Typ Training. Und wenn ich jetzt hier so eine Property habe, dann kann ich in der AppKomponente hier diese Zuweisung machen.
Und ich mache jetzt hier ein DataBinding, so wie wir es hier oben gemacht haben, und in die TrainingListKomponente die ganze Liste reinreichen, sage ich jetzt halt hier unten. Ich mache hier ein Binding auf eine Property Training und reiche dort die selectedData rein. Jetzt muss ich in der DetailKomponente hergehen
und das Gleiche machen, was wir gerade schon mal gemacht hatten. Ich mache mir hier ein Feld Training vom Typ Training und markiere das mit addInput. Jetzt muss ich noch meine Imports machen. Input und hier unten drunter import
from training.model. Die TrainingKlasse. Und da fehlt noch die aufrufende Klammer. Die muss bei Annotationen in TypeScript immer dahinter sein. Und wenn wir jetzt zurückgehen, dann sehen wir noch nichts. Warum sehen wir nichts?
Ah, weil ich das Template noch gar nicht angepasst habe. Das heißt, wir müssen hier noch in das Template der DetailKomponente gehen und hier jetzt zum Beispiel einen Diff mit training.name und unten drunter machen wir noch einen Diff
mit training.description. Und jetzt knallt es. Jetzt kriegen wir eine Exception. Das ist auch eigentlich ganz verständlich, wenn man sich überlegt, was im Hintergrund passiert. Die AppKomponente wird instanziert, hat zwei Felder. In dem einen steht die Liste, in dem anderen steht nichts.
Und dieses nichts undefined reichen wir an die DetailKomponente weiter. Und die DetailKomponente greift jetzt per DataBinding darauf zu und damit haben wir einen klassischen NullPointer produziert. Wir können jetzt hier den sogenannten ElvisOperator verwenden. Wir können jetzt hier einfach ein Fragezeichen hinmachen. Und damit sagen wir, wenn etwas da ist, dann greift bitte auf dieses Name zu.
Wenn nichts da ist, dann greift auch nichts drauf zu, damit es halt keinen Fehler gibt. Fragt mich nicht, warum das Ding ElvisOperator heißt. Also es heißt auch SafeNavigationOperator, wahrscheinlich ist es halt wegen der... So, da ist unsere Anwendung. Und wenn ich jetzt hier eins anklick, dann werden unten immer schön die Details dargestellt.
Ihr könnt jetzt hier mit CSS noch ein bisschen stylen, dass die Hand, oder der Mauspointer ein Cursor wird, wenn man da drüber ist. Das sparen wir uns jetzt. Was wir dann noch machen wollen, ist ein ClassBinding verwenden. Wir wollen nämlich in der Liste noch hervorheben, welches Training selektiert ist. Dafür gibt es die Bindings auf Klassen. Und hier kann ich jetzt sagen,
die Klasse Selected soll bitte gesetzt werden, wenn diese Condition, die ich hinten dran angebe, erfüllt ist. Das ist das ngClass aus Angular 1. ngClass gibt es auch weiterhin. Das heißt, man kann weiterhin mit dieser hässlichen JSON-Notation in einem String, in einem Feld arbeiten. Hier, das finde ich deutlich schöner, weil man jetzt verschiedene Klassen halt auch
untereinander schreiben kann. Und das bei mehreren Klassen nicht so unübersichtlich wird. Und hier können wir jetzt einfach sagen, Training. Dieses aktuelle Training ist gleich. Und jetzt müssen wir das mit irgendwas vergleichen. Und ich mache es mir jetzt wieder einfach. Nehmt das nicht als Best Practice für einen Applikationsdesign. Ich möchte euch nur Möglichkeiten zeigen, was man mit Angular machen kann. Das heißt, wir speichern uns einfach in
dieser Listen-Komponente, welches denn jetzt das zuletzt selektierte war. Sagen Training. Und dann gehe ich zurück in meine Komponente, lege mir hier parallel zu dem TrainingSelected noch ein Feld SelectedTraining an, vom Typ
Training. Und hier unten, wenn ich das Event vorher sage ich halt zusätzlich noch SelectedTraining ist das Training, das ich übergeben gekriegt habe. Damit wird jetzt an diesem Element die Klasse gesetzt, aber halt nur an diesem einem Element in dem Repeater, für das diese Condition zutrifft. Und wenn ich jetzt hier in die CSS-Datei
gehe und noch ein bisschen Styling mache, Selected soll halt Background Light Grey kriegen. Muss man ein bisschen aufpassen. Das CLI hat einen Bug, wenn man CSS ändert, dann findet kein automatisches Live-Reload statt. Das heißt, da muss man nochmal neu laden. Und wenn ich es jetzt hier
anklick, dann wird entsprechend immer diese Klasse gesetzt und die Klasse sorgt halt dafür, dass es jetzt irgendwie hervorgehoben wird. Das ist das eine, was man mit den Inputs und Outputs und Property Bindings machen kann. Wenn wir zurück auf die Agenda
gucken, dann haben wir jetzt diesen Teil hier oben gemacht und wir wollen jetzt den nächsten Teil machen, nämlich dass wir einen Service und Dependency Injection verwenden. Das ist natürlich ein völliges Un-Ding, das unsere App-Komponente die Daten bereitstellt und das hard codierte Mock-Daten sind. Ich mache mal hier einfach alles zu. Und was wir stattdessen machen wollen, ist einen Service verwenden.
Dafür können wir wieder in die Konsole gehen und sagen mit NG Generate Service Training dass wir den Training Service haben möchten. Und diesen Training Service haben wir jetzt hier unten. Er generiert uns auch immer direkt irgendwelche Test-Dateien mit raus für Spektests,
damit wir Test-Driven entwickeln können. Kann man mit NG Test in einer extra Konsole die Test-Ausführung starten und dann immer schön auch die Tests dazu schreiben, ob man jetzt Test-First, also Test-Driven entwickelt oder wie auch immer, ist dann jedem selber überlassen. Wir haben hier unsere Training Service-Klasse
und die soll jetzt eine Get-All-Methode bereitstellen. Und hier kommt jetzt das erste Mal dann etwas ins Spiel, was wir in Angular 2 sehr prominent drin haben. Wer hat schon mit RxJS oder Observables gearbeitet? Okay, zaghaft. Kann ich jetzt nicht so detailliert drauf eingehen, weil da
könnte man alleine schon ein paar Stunden drüber reden. Ist ein Pattern, was quasi die Fortsetzung von Promises ist. Ein Promise kann genau einmal aufgelöst werden, ist für Asynchronität gut, aber kann halt nur einmal aufgelöst werden. Observable ist ein Stream, ein asynchroner Stream und der kann halt mehrmals Daten liefern. Das heißt, wir sagen jetzt hier, da kommt ein Observable
von Training Array zurück und dieses Observable müssen wir jetzt natürlich wieder importieren. Das kommt aus RxJS slash AX. Also kann ich wirklich nur empfehlen, sich das anzugucken. Und da kommt das Observable raus.
Und hier innen drin wollen wir jetzt eben ein Observable zurückgeben. Wir bleiben erstmal bei den Mockdaten. Und um diese Mockdaten zur Verfügung stellen zu können, verwende ich jetzt ein sogenanntes Subject. Das ist quasi der Teil, der in einer Promise Implementierung diese Funktion ist, die wir in New Promise
reinreichen, wo die Reject und Resolve Funktion reinkommt. Ich sage jetzt hier Subject ist vom Typ Replay. Subject Subject. Ich mache direkte Instanzierung daraus. New Replay Subject. Und das ist natürlich
auch wieder mit Generics Training Array. Und wir sagen hier beim Replay Subject es soll Replayen. Und zwar genau einen Wert, den wir einmal reingesteckt haben. Replay Subject und Training müssen wir noch importieren.
Punkt slash Training Punkt Model. Und von dort bitte Training. Und hier unten können wir jetzt einfach sagen This Punkt Subject Punkt As Observable. Und jetzt müssen wir irgendwo noch unsere Daten reinstecken. Das können wir jetzt hier im
Konstruktor machen. This Punkt Subject Next. Damit sagen wir dem Subject Hey, hier sind diesem Observable hier sind neue Werte. Und dort reichen wir jetzt einfach genauso wie vorher auch unsere Training Konstante rein. Das heißt wir ändern noch gar nichts dran, dass wir Mockdaten verwenden. Sondern wir lagern das Ganze nur in einen Service aus.
Damit unsere Komponente unabhängig von der Datenhaltung ist. Training Punkt Mock und von dort die Trainings. Achso, da unten fehlt noch das Return. Fertig ist unser Service, der jetzt die Datenbeschaffung und Datenhaltung irgendwie kapselt. Wir haben
einfach diese Get All Methode. Die können wir in der Komponente verwenden. Und wir können nachher diesen Service einfach umbauen und hier HTTP Kommunikation reinmachen. Auch dort geben wir dann wieder ein Observable zurück. Und dann brauchen wir die Schnittstelle nicht mehr ändern. Deswegen auch die Empfehlung von vorne rein mit asynchronen Schnittstellen in Services arbeiten. Ihr werdet früher oder später sowieso HTTP
Kommunikation machen. Und wenn ihr die Asynchronität einmal an der Backe habt, dann werdet ihr sie auch nicht mehr los. Ihr werdet sie erst beim Data Binding los. Wir haben jetzt den Service hier geschrieben. Wir wollen ihn natürlich hier in der App Komponente verwenden. Das heißt, anstatt hier diese Daten hart codiert reinzuschreiben, wollen wir stattdessen jetzt den
Service verwenden. Dafür schreiben wir uns einen Konstruktor. Im Konstruktor lassen wir uns jetzt diesen Service geben. Es gibt in TypeScript diese Schreibweise, dass man vor einen Konstruktorparameter einfach einen Access Modifier vorne dran schreiben kann. Das sorgt dafür, dass es automatisch ein Feld gibt und das Feld mit dem Wert vom Konstruktorparameter initialisiert
wird. Das heißt, ich sage hier, ich hätte gern ein Feld Service und das soll vom Training Service sein. Dann ist mein Konstruktor quasi leer. Und hier oben sage ich jetzt Import
aus Trainings Training Punkt Service und von dort den Training Service. Wenn ich diesen Service habe, dann ist es best practice, dass man das nicht im Konstruktor initialisiert, sondern Initialisierung der Komponente und Dependency Injection voneinander trennt. Es gibt
ein OnInit Interface von Angular, das wir hier oben importieren können. Das sind die sogenannten Lifecycle Hooks. Und dann kann ich hier unten mir im Konstruktor einfach nur diese Dependency geben lassen in einem Feld speichern. Das ist sehr leichtgewichtig. Das ist gut für die Testbarkeit. Und dann habe ich hier meine ngOnInit
Methode. Und in der OnInit Methode mache ich dann meine Heavy Sachen, dass ich hier sage, das Punkt Service, Punkt GetAll und daran subscribe ich mich dann. Das soll es noch aufrufen. Subscribe. Und wenn ich mich hier dran subscribe, kriege ich jetzt hier
die Trainings reingereicht und dann kann ich this.list, hieß es bei mir jetzt, this.list like Trainings. Das ist die Asynchronität. Das sollte, wenn ihr schon mal mit Promises gearbeitet habt, ein bisschen vertraut aussehen. Da würde es halt hier then heißen und nicht subscribe. Bei den Observables
ist es eben, dass das mehrfach auftreten kann und ich hier immer informiert werde, wenn jetzt neue Daten zur Verfügung stehen. Und dann übernehme ich sie immer in diese Liste hier oben. Das heißt, die Initialisierung dort können wir jetzt wegschmeißen. Und wenn wir das jetzt so laufen lassen, dann kriegen wir wieder Fehler. Bei den Fehlern immer ganz nach oben scrollen und jetzt meckert er halt, dass er keinen Provider für diesen Training Service gefunden
hat. Wir haben ja auch die Dependency Injection noch gar nicht konfiguriert. Wir haben jetzt einfach nur einen Service, eine Klasse, irgendwo hingelegt. Kann Angular ja nicht wissen, was damit gemeint ist. Deswegen gehen wir jetzt her und konfigurieren die Dependency Injection. Das kann man auf verschiedenen Ebenen machen. In Angular 1 hatten wir die Situation, dass es einfach nur einen Injector gab,
der für alles zuständig war und wenn man mehrere Sachen mit dem gleichen Namen registriert hat, dann haben die sich munter gegenseitig überschrieben. Das geht in Angular 2 nicht mehr, bzw. nicht mehr so gut, sondern wir haben einen hierarchischen Injectionsmechanismus. Das heißt, auf jeder Komponenten-Ebene wird ein Kind, kein Kind-Injector
von der Komponente oben drüber erzeugt. Das heißt, wir können hier in unsere App-Komponente gehen und das Ganze hier drin machen, indem wir hier ein Feld Providers angeben. Was ich jetzt machen möchte, ist, dass wir es in der App-Komponente machen. Da kann man es nämlich auch nicht App-Komponente, sondern in der Bootstrap-Methode bei unserem Aufruf
hier unten in der Main.ts, können wir hier hintendran ein Array von Providern angeben und da gibt es jetzt verschiedene Notationen. Die einfachste Notation wäre jetzt einfach zu sagen Training, Service. Wir geben einfach die Klasse an und importieren die natürlich. Hier oben noch Import Statement from .app
Trainings slash Training Service und von dort die Training Service Klasse. Anstatt einfach hier nur die Klasse anzugeben, könnten wir jetzt hier auch ein Objekt angeben mit Provide, dann die Klasse und dann Use Class und dann nochmal die Klasse. Das hier vorne ist das Token,
unter dem es nachher angefordert werden kann. Ich habe in meiner App-Komponente gesagt, ich hätte gerne den Training Service und hier sage ich jetzt, wenn immer jemand nach Training Service fragt, nimm bitte das und instanziere diese Klasse. Wird auch als Singleton instanziert, ich kann aber durch diesen hierarchischen Injektionsmechanismus dann auch mehrere Instanzen erzeugen. Alternative wäre
hier zum Beispiel dann Use Factory oder Use Existing, wo man dann eben auch mit abstrakten Klassen arbeiten kann und dann so Bindings umbiegen kann, dass man sagt, da ist jetzt zwar ein Binding und jemand fragt nach einem Logger, aber es soll jetzt nicht der Logger verwendet werden, sondern der Console Logger oder der HTTP Logger.
Use Existing braucht man dann auch viel in Tests, um solche Bindings dann umzubiegen. Das heißt, wenn ich das jetzt so angegeben habe, dann funktioniert es wieder und jetzt haben wir quasi Asynchronität drin, wobei die Asynchronität natürlich jetzt nicht wirklich da ist, weil wir ja noch gar keine Backend-Kommunikation machen. So,
jetzt spiegele ich mal kurz. Die Backend-Kommunikation mal wir dann aber jetzt als nächstes einbauen. Das heißt, wir gehen in unseren Service. Unsere Komponenten interessieren sich jetzt gar nicht mehr dafür. Dieses Subscribe hier unten ist eine potenzielle
Stelle, an der wir aufpassen müssen, dass wir keinen Speicherlack erzeugen. Wann immer man eine Subscription macht, gibt man ja eine Funktion. Das hier hinten ist eine Funktion an jemand anders. Und hier innen drin habe ich eine Referenz auf Sys. Das heißt, wenn diese App-Component abgeräumt wird und mein Service langlebiger ist, dann hätte ich einen Speicherlack erzeugt, weil
innen drin noch eine Referenz in dem Service gespeichert ist in dieser Funktion auf meine Komponenten-Klasse. Das heißt, man müsste eigentlich hier vorne diese Subscription entgegennehmen. Bei Subscribe kommt immer eine Subscription zurück. Und dann würde es hier unten ein ng-on-destroy geben. Und dort könnte ich dann sys.subscription.
ansubscribe aufrufen. Nur, dass ihr es mal gehört habt.
Genau, bevor das getall irgendwas emittet, bleibt dieses Training hier oben einfach leer. Oder dieses List. Das heißt, ich habe ein Feld gemacht, wenn diese Komponente instanziert wird, ist dieses Feld undefined.
Und solange mein Händler hier hinten nicht aufgerufen wird, ist es auch undefined. Damit hätten wir wieder das gleiche Problem theoretisch wie bei der Detail-Komponente, dass wir undefined reinreichen. In dem Fall knallt es halt nicht, weil wir, wenn wir in die Listen-Komponente reingucken, hier nicht davon abhängig sind, ob in dem Trainings was drin steht. Dieses ng-for ist das einzige
Binding, das wir auf diese Liste machen, die uns reingereicht wird. Und das schützt sich automatisch davor, dass es hier nicht über etwas iteriert, was nicht da ist. Das heißt, in dem ng-for ist schon so ein Check drin, dass wir hier keine Exception provozieren. Aber prinzipiell wäre es genau das gleiche Problem mehr. Also, das können wir recht einfach verdeutlichen,
wenn wir hier oben irgendwie einen Diff drüber machen und hier sagen we offer Trainings .links Trainings. Dann wird es jetzt knallen, weil wir jetzt wieder einen... Na, hat das nicht gespeichert?
Das hatte ich noch nicht gespeichert. Warum findet er mein geändertes... Ja, die sollte hier raus sein.
Und selbst wenn, würde sie ja von dem Wert, der hier unten kommt, überschrieben. Vielleicht hat er aber auch das noch nicht gespeichert. Ansonsten, was auch ab und zu mal hilft, ist das ng-surfkilling. Was hat er jetzt für einen Fehler geworfen? Vielleicht habe ich irgendwo
einen Compile Error. Ja, das passiert. Wir haben jetzt hier einen Compile Error in der Testdatei, denn wenn wir in die Testdatei jetzt reingehen, in dieser AppComponentSpec, ist hier unten noch der Test auf dieses AppTitle und das gibt es ja nicht mehr. Das heißt, ich schmeiße einfach diesen Test dazu mal raus. Dann sollte mein Compiler wieder grün sein und wenn wir jetzt neu
laden, dann hat er das Template immer noch nicht. Bin ich irgendwie in der falschen Datei gelandet? Training List. Ich glaube, ich weiß, was das Problem ist. Der hat hier doch
Disable Cache. Jetzt ist es drin. Fünf. Aber warum hat es noch nicht geknallt? Das kann sein, weil wir jetzt so Pseudoasynchronität drin haben, dass unser Händler da unten aufgerufen wird, bevor das Data Binding das erste Mal durchläuft. Wenn wir jetzt HTTP-Kommunikation reinmachen würden und ich tatsächlich ein bisschen Verzögerung im Server oder sowas hätte, dann würde es knallen. Dann
wäre es ein leeres Array. Nee, solange noch nichts emittet wurde. Ach so, danke. Es ist nicht ein leeres Array. Also Frage war, ob das nicht ein leeres Array ist, sondern hier in der App Komponente haben wir einfach erstmal nur ein Feld. Wir sagen hier nur, dass es vom Typ Training Array ist. Ein leeres Array wäre, wenn ich jetzt hier halt initialisiere. Dann wäre es ein
leeres Array. So ist es einfach nur undefined, solange bis hier unten diese Funktion einmal aufgerufen wird. Funktioniert jetzt, weil wir eben keine wirkliche Asynchronität drin haben. Wir sollten uns an der Stelle jetzt trotzdem hier in dem Template hier oben wieder über den Elvis-Operator schützen, dass wir das
halt nicht abfragen. Da stellt sich natürlich dann auch die Frage, ob man hier dann Reoffer Trainings anzeigen möchte, was in dem Fall ja vom Satzbau her sogar noch sinnvoll ist und einen kompletten Satz ergibt. Vielleicht würde man dann eher die Komponenten von außen mit einem NGIF schützen, dass sie gar nicht angezeigt werden, wenn keine Daten da sind. Da hat man verschiedene Möglichkeiten.
Was wir jetzt eigentlich machen wollen, ist hier dieses lokale Datenhaltung und sowas loswerden. Das heißt, wir schmeißen hier oben dieses Subject wieder weg und den Konstruktor brauchen wir so in der Form auch nicht mehr, sondern was wir hier jetzt sagen, wir wollen ein privates Feld haben mit dem HTTP-Service. Das kommt aus
import at angular slash http und von dort holen wir uns den HTTP-Service. Und hier unten bei get all sagen wir jetzt eben, dass wir return und jetzt machen wir mit this.http
.get einen Request zum Backend. Dort läuft unter ap training mein rest endpoint und hier muss ich jetzt noch ein bisschen Zwischenverarbeitung machen. Ich sage jetzt hier, dass ich ein Mapping mache und da kommt jetzt der Vorteil von dieser API von dem observable. Wir können
jetzt hier einfach eine Funktion angeben. Wir nehmen hier die response entgegen und sagen response .json. Das ist nicht auf dem Angular Mist gewachsen, sondern das ist die API von der neuen Fetch API, mit der man im Browser asynchroner Request machen
soll. Und bei mir im Backend ist noch dieser Security Mechanismus mit drin, dass man keine Arrays per JSON direkt übertragen soll, weil das die Möglichkeit bietet für einen man in the middle ausführbaren Code zu injecten. Deswegen übertragen wir immer ein Objekt statt eines Arrays und wenn wir halt ein Objekt übertragen, aber eigentlich ein
Array übertragen wollen, machen wir in dem Objekt einfach eine data property. Das heißt, wir machen hier ein Mapping und lesen aus der response einfach nur die Daten und dann aus den Daten das Data fällt. Und im Prinzip würde das schon reichen. Das heißt, wir könnten es einfach so schreiben. Was ich jetzt
auf next date greife mir gar nicht zu. Problem an der Stelle wäre jetzt noch, dass wir hier im Model gesagt haben, dass next run vom Typ date ist und per json kann kein date Objekt überzeugt werden. Das heißt, da wird ein ISO String übertragen. Wenn wir ins Backend reingucken,
ach so, den Code habe ich jetzt nicht. Hier dann wird da eigentlich auch mit date Objekten gearbeitet, aber die müssen ja sehr realisiert werden. Das heißt, ich würde jetzt hierher gehen und nochmal ein Mapping unten drunter machen und halt sagen, ok, jetzt das Array durchlaufen und in jedem Element dieses next run wieder von einem date String in ein date Objekt umwandeln und würde dieses Objekt dann zurückgeben.
Jetzt haben wir wieder die gleiche Situation, dass er jetzt gleich meckern wird, dass er diesen Provider für das HTTP nicht kennt. Hier oben räume ich noch ein bisschen auf. Ich gehe wieder in meine App-Component nicht, sondern in die Main.ts und füge jetzt hier einen weiteren Provider hinzu. Glücklicherweise hat Angular das eingeführt, dass es dort
dann so Konstanten gibt. Das heißt, wir sagen hier add Angular slash HTTP und dort gibt es die HTTP Providers und die können wir jetzt hier unten einfach hinzufügen, damit dieser Service überhaupt zur Verfügung steht. Jetzt wird es im Browser erstmal knallen, weil ich mein Backend nicht laufen habe.
Das mache ich jetzt hier mal schnell wieder. Das ist einfach so eine ganz kleine Dummy Implementierung, die auch hart codierte Testdaten zurückgibt und ein kleines REST Interface zur Verfügung stellt. Jetzt haben wir ein Problem. Wir haben zwei Server, einen Development Server, der das UI ausliefert und einen Backend Server, der auf Port 3000 läuft. Wir
müssten jetzt in unserem Service hier eigentlich hergehen und wo ist mein Service? Hier im Training Service halt nicht eine relative URL aufrufen, sondern hier halt auf HTTP Localhost 3000. Zum einen wollen wir das natürlich nicht hart codieren. Zum anderen hätten wir dann ein Course Problem. Das heißt, das Security Modell
vom Browser verbietet, dass diese JavaScript Datei, die von einem Server geladen wurde, einen Request zu einem anderen Server macht. Da haben wir zwei Möglichkeiten. Entweder wir konfigurieren halt die Header entsprechend, dass die beiden Server bestätigen, dass die Kommunikation stattfinden darf. Oder wir machen es uns einfach und arbeiten mit einem Proxy. Ich kille den ng-serve und hänge hier einfach minus minus
proxy hinten dran und sage ihm, wenn irgendwas kommt, das du nicht bedienen kannst, dann geh bitte zu Localhost 3000. Und damit können wir den Request auf den eigentlichen Server machen und der... Oh, danke. Da hat die Null gefehlt. Nochmal starten.
Und wenn wir jetzt in den Browser gehen und hier Fehler hatten, weil er entsprechend hier dieses... nicht gefunden hat, dann merkt er jetzt hier noch, dass map keine Funktion ist. Das liegt da dran, dass rx eine sehr große Bibliothek ist und man
nicht unbedingt komplett rx haben möchte. Allerdings habe ich hier oben aus rx importiert. Das heißt, eigentlich wird die ganze Bibliothek geladen. Man könnte auch hier von observable importieren. Dann würde man über die Importstatements nicht rx anweisen, wirklich die ganze Bibliothek
zu laden. Dann müsste ich jetzt hier unten sagen, dass es dieses map nicht gibt. Wenn ich es wieder zurückmache, dann ist er eigentlich glücklich damit. Map, jetzt gucken wir nochmal. Ansonsten mache ich hier oben noch den import, um auf Nummer sicher zu gehen,
dass wir hier ein rxjs slash add operator slash map und das hier oben kann weg. So, jetzt geht's. Und wenn wir hier reingucken und ich jetzt hier nach api
filter, dann sehen wir hier unten eben auch, dass der Request vom Backend kam und hier mein Array zurückkam. Jetzt können wir immer noch hier selektieren. Wir arbeiten mit den Daten, die wir schon geladen haben. Und was jetzt natürlich noch ein bisschen schöner wäre, dass man irgendwie sagt, naja, wenn eins selektiert wird oder wenn wir die Liste laden, dann wollen wir nicht die
ganze Liste mit heavy Objekten laden, sondern wir laden halt nur eine Liste mit name properties, weil wir ja nur die in der Liste brauchen. Vielleicht noch ID oder so, damit wir irgendwie navigieren können. Und wenn wir auf die Detailansicht draufgehen, dann laden wir erst das einzelne Objekt komplett mit
getByID oder so was. Was wir natürlich auch noch machen wollen, wo jetzt die Zeit ein bisschen knapp wird, ist, dass wir Routing einbauen. Das heißt, wir wollen nicht die ganze Zeit die Situation haben, dass wir beides zusammen anzeigen, sondern wir wollen hier oben so was anbieten, dass wir hier auf Training gehen, dann kriegen wir die Liste angezeigt. Und wenn wir auf Training 1, 2 oder 3 oder so was gehen, dann kriegen wir nur die Detailansicht
angeboten. Mit dem Routing würde ich jetzt einfach mal noch weitermachen. Gibt es im Moment Fragen dazu? Ansonsten würde ich die Zeit einfach voll ausschöpfen und noch weiter programmieren. Wenn keine Fragen sind, dann mache ich weiter. Wir wollen den Routing-Mechanismus verwenden. Da brauchen wir ein bisschen mehr Code jetzt für.
Wir legen uns eine Datei namens app.routes.ts an. Das ist einfach best practice. Und da drin sagen wir, wir importieren. Wir machen ein Import Statement from addangular slash router. Und von dort importieren wir die
Route config. Und wir erzeugen uns jetzt hier ein Export. Wir brauchen gar keinen Export hinmachen. Wir machen einfach ein let routes gleich vom Typ
router config. Und das soll ein Array sein. Und eine router config ist dann eben, dass wir ein Objekt haben. Und dieses Objekt hat eine pass property. Dort sage ich jetzt training. Und wenn training aufgerufen wird, dann soll bitte als component meine training
list component angezeigt werden. Und ich mache mir eine zweite Route. Dort gebe ich als path dann training slash doppel.id mit Parameter. Und sage dann component soll bitte die training details
component sein. Die muss ich jetzt hier oben natürlich wieder importieren. Das ist ein bisschen doof, dass die Imports heute jetzt nicht funktionieren. Das heißt, ich muss hier in training slash training list
rein und von dort die training list Komponente. Und das gleiche noch für die details. Dann haben wir unsere Routen konfiguriert. Jetzt müssen wir Angular noch irgendwie mitteilen, dass es diese Routen gibt. Und das macht man auch wieder über einen provider. Das heißt, wir würden hier export
app route providers hinschreiben. Und dort gibt es dann eine Provide Router Funktion, die wir uns aufrufen können. Das heißt, wir machen uns hier einen Array. Und rufen Provide Router auf. Und dem geben wir als
Konfiguration unsere Routen mit. Und von ihm kriegen wir einen fertig konfigurierten Provider für den Router Service. Und dieser Router Service kriegt dann unsere Routen übergeben und weiß dann, was im Hintergrund passieren soll. Hier müssen wir noch das vorne dran machen. Diesen Provider müssen wir jetzt natürlich wieder einbinden.
Wir gehen hierher und sagen hier, dass wir gerne die app route provider mit einbinden würden. Oder hatte ich es router genannt? Import statement from .slash app slash app.routes und von dort die
app route providers. Ah, da fehlt es. Damit haben wir den Mechanismus an sich konfiguriert. Wenn wir jetzt in unserer Route angegeben haben, dass wenn eine bestimmte URL aufgerufen werden soll, dann eine bestimmte Komponente angezeigt
werden soll, dann müssen wir natürlich noch irgendwie sagen, wo soll diese Komponente denn eigentlich hin? Das gibt es ja in Angular 1, den Mechanismus genauso. Mit der UI View oder NG View, je nachdem ob man den UI Router oder den NG Route verwendet. Das heißt, wir schmeißen hier in der App Komponente unsere Liste und unsere
Detailansicht raus und sagen stattdessen jetzt Router Outlet. So heißt das Ganze dann in Angular 2. Jetzt müssen wir wieder dran denken, was ich vorhin erzählt hatte. Wenn wir die Komponente verwenden wollen, müssen wir sie bekannt machen. Wir verwenden in dieser App Komponente jetzt diese beiden nicht mehr, sondern hier müssen wir jetzt die
Router Directives einbinden und die müssen wir natürlich wieder importieren. Das heißt wir machen uns wieder unser Import Statement from at Angular Angular slash Router und dort gibt es die Router
Directives und wenn wir die eingebunden haben, dann können wir mal gucken, wie es im UI aussieht. Da fliegt noch irgendwas. Und zwar hatte ich das hier gerade so aufgerufen und es fehlt noch die Route was passieren soll, wenn nichts aufgerufen wird. Da können wir einfach wieder in den Code zurückgehen, in unsere
App Routes und eine dritte Route definieren und dort sagen wir, wenn der Pfad leer ist, dann mach bitte ein redirect to und dort geben wir dann slash training an, damit er auf dieser Listenansicht oben landet.
Und wenn wir dann reingehen und er neu geladen hat, dann sagt er invalid route configuration. Ah, genau. Ein Punkt dazu noch. Der Routing-Mechanismus unterstützt hierarchische Strukturen. Das heißt wir können Kind routen und sowas definieren. Deswegen wird dieser Pfad hier nicht als
equals gecheckt, sondern als start with. Wenn wir aber ein start with mit einem leeren String machen, ist das ein bisschen ungeschickt, weil jeder String fängt mit einem leeren String an. Deswegen gibt es die Möglichkeit, das hier zu konfigurieren, wie verglichen werden soll. Da gibt es diese pass match property und da steht normalerweise Prefix mit drin. Und wir machen jetzt full, damit das
eben komplett gecheckt wird. Und dann sagt er jetzt cannot find outlet. Habe ich die Datei nicht gespeichert? Das ist ein bisschen dumm. Ja, die Datei ist nicht gespeichert. Das ist eine dumme Angewohnheit von dem Visual Studio Code, dass er immer nur die Datei gespeichert, in der man gerade ist. Wenn man zu viele Dateien ändert, es gibt natürlich auch ein
save all, aber da gibt es keinen Shotcut für. Man kann aber in den User-Config einstellen, dass wenn man den Fokus wechselt, dass dann automatisch alles gespeichert wird. Da sind jetzt unsere Listenansicht. Jetzt haben wir nur ein Problem. Unsere App-Komponente bindet die Listenansicht nicht mehr explizit ein. Die kann keine Daten reinreichen und der Routing-Mechanismus wird wohl kaum hergehen und diese Daten
reinreichen. Das heißt, wir müssen das Ganze jetzt so umbauen, dass unsere Listenansicht sich die Daten halt selber beschafft. Das heißt, das was jetzt im Moment hier in unserer App-Komponent drin ist, dass wir uns den Konstruktor geben lassen und diese im Konstruktor den Service geben lassen und in der ng-on-init, das packen wir jetzt hier in unsere Liste rein,
ob es in der App drin bleibt oder nicht, spielt erstmal gar keine Rolle. Das heißt, ich ersetze die leeren Implementierungen hier und dann soll das hier auch nicht mehr List heißen, sondern Trainings. Dort hatte ich es richtig benannt. Den müssen wir da oben noch importieren. Importstatement from slash Training.
Service. Eins raus navigieren. Training Service. Und dann sollte unsere Listenansicht eigentlich schon wieder funktionieren. Da sind unsere Sachen. Jetzt funktioniert es natürlich nicht mehr mit Anklicken und Highlighten und unten darstellen, sondern wir müssen jetzt hier
einen Link einbauen. Dafür müssen wir uns hier wieder diese Router Directives importieren. Import from at Angular slash Router. Die Router Directives, die registrieren wir uns hier unten über Directives Router Directives
und wenn wir die da verwendet haben, dann können wir hier ins HTML gehen und können statt dieses Klicks hier jetzt hier unten ein Artec draus machen. Wenn wir jetzt hier hard codiert einen HREF angeben, dann ist das ein bisschen ungeschickt. Angular kann zwei Mechanismen
zum navigieren. Einmal Path Location und einmal Hash Location. Das, was es in Angular 1 auch schon gab, je nachdem, ob man diese HTML5 Variante verwenden möchte oder nicht. Das ist das letzte, was ich jetzt noch eben mache. Wir haben hier einen Router Link und dem können wir eben mitgeben, dass wir einmal zu Training navigieren möchten
und dann zu unserer Training Punkt ID. Und wenn ich jetzt diesen Namen hier unten dazwischen schreibe, dann sollten wir hoffentlich hier Links kriegen. Was hat er jetzt? Ich habe die Datei wieder nicht gespeichert.
So und jetzt habe ich hier meine Links und könnte jetzt theoretisch zu der Liste navigieren. Hier habe ich jetzt einen Slash vergessen. Und da wären wir bei der Detail-
die wir jetzt halt wieder noch entsprechend bearbeiten müssen, dass die Detailansicht die Daten sich auch selber holt und dann irgendwie im Service eine Get-By-ID anbieten, die dann vom Backend die entsprechenden Daten lädt und zurückgibt. Leider reicht die Zeit nicht mehr dafür. Ich hoffe, ich konnte euch ein bisschen einen Eindruck vermitteln, wie so die Entwicklung mit Angular 2 abläuft. Wenn es Fragen gibt, stehe ich gerne jetzt gleich auch danach hier
oder draußen dann noch zur Verfügung. Oder ihr könnt mich gerne kontaktieren, entweder über unseren Twitter-Kanal oder mich direkt anschreiben. Hier vorne steht auch noch mal meine E-Mail-Adresse. Wen es interessiert, ich habe ein bisschen Werbematerial dabei. Wer einen Trending-Interesse hat oder sowas,
kann sich gerne bei mir melden. Ansonsten wünsche ich euch noch eine schöne Konferenz und einen schönen Abend und danke für die Aufmerksamkeit.