Een webapplicatie bouwen met Spring Boot en Angular

1. Overzicht

Spring Boot en Angular vormen een krachtige tandem die uitstekend werkt voor het ontwikkelen van webapplicaties met een minimale footprint.

In deze tutorial we gebruiken Spring Boot voor het implementeren van een RESTful-backend en Angular voor het maken van een JavaScript-gebaseerde frontend.

2. De Spring Boot-applicatie

De functionaliteit van onze demo-webapplicatie zal inderdaad behoorlijk simplistisch zijn. Het wordt alleen versmald tot het ophalen en weergeven van een Lijst van JPA-entiteiten uit een H2-database in het geheugen, en blijvende nieuwe via een gewoon HTML-formulier.

2.1. De Maven-afhankelijkheden

Dit zijn de afhankelijkheden van ons Spring Boot-project:

 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 runtime 

Merk op dat we het hebben opgenomen spring-boot-starter-web omdat we het zullen gebruiken voor het maken van de REST-service, en spring-boot-starter-jpa voor het implementeren van de persistentielaag.

De H2-databaseversie wordt ook beheerd door de Spring Boot-ouder.

2.2. De PPV-entiteitsklasse

Laten we, om snel een prototype te maken van de domeinlaag van onze applicatie, een eenvoudige JPA-entiteitsklasse definiëren, die verantwoordelijk is voor het modelleren van gebruikers:

@Entity openbare klasse Gebruiker {@Id @GeneratedValue (strategie = GenerationType.AUTO) privé lang ID; private laatste String naam; privé laatste String e-mail; // standard constructors / setters / getters / toString} 

2.3. De UserRepository Koppel

Omdat we basis CRUD-functionaliteit nodig hebben op het Gebruiker entiteiten, moeten we ook een UserRepository koppel:

@Repository openbare interface UserRepository breidt CrudRepository uit {} 

2.4. De REST-controller

Laten we nu de REST API implementeren. In dit geval is het gewoon een eenvoudige REST-controller.

@RestController @CrossOrigin (origins = "// localhost: 4200") openbare klasse UserController {// standaardconstructors privé definitief UserRepository userRepository; @GetMapping ("/ users") openbare lijst getUsers () {return (List) userRepository.findAll (); } @PostMapping ("/ users") void addUser (@RequestBody User-gebruiker) {userRepository.save (gebruiker); }} 

Er is niets inherent complex in de definitie van de UserController klasse.

Het enige implementatiedetail dat het vermelden waard is, is natuurlijk het gebruik van de @CrossOrigin annotatie. Zoals de naam al aangeeft, maakt de annotatie Cross-Origin Resource Sharing (CORS) op de server mogelijk.

Deze stap is niet altijd nodig. Omdat we onze Angular-frontend inzetten voor // localhost: 4200 en onze Boot-backend naar // localhost: 8080, de browser zou anders verzoeken van de een aan de ander weigeren.

Met betrekking tot de controllermethoden, getUser () haalt alle Gebruiker entiteiten uit de database. Evenzo is het Voeg gebruiker toe() methode behoudt een nieuwe entiteit in de database, die wordt doorgegeven in de hoofdtekst van het verzoek.

Om de zaken eenvoudig te houden, hebben we opzettelijk de controllerimplementatie weggelaten die Spring Boot-validatie activeert voordat een entiteit wordt gehandhaafd. Tijdens de productie kunnen we gebruikersinvoer echter niet vertrouwen, dus validatie aan de serverzijde zou een verplichte functie moeten zijn.

2.5. Bootstrapping van de Spring Boot-applicatie

Laten we tot slot een standaard Spring Boot-bootstrapping-klasse maken en de database vullen met een paar Gebruiker entiteiten:

@SpringBootApplication openbare klasse Toepassing {openbare statische leegte hoofd (String [] args) {SpringApplication.run (Application.class, args); } @Bean CommandLineRunner init (UserRepository userRepository) {return args -> {Stream.of ("John", "Julie", "Jennifer", "Helen", "Rachel"). ForEach (name -> {User user = new Gebruiker (naam, naam.toLowerCase () + "@ domein.com"); userRepository.save (gebruiker);}); userRepository.findAll (). forEach (System.out :: println); }; }}

Laten we nu de applicatie starten. Zoals verwacht zouden we een lijst met Gebruiker entiteiten die bij het opstarten naar de console worden afgedrukt:

Gebruiker {id = 1, naam = John, [e-mail beschermd]} Gebruiker {id = 2, naam = Julie, [e-mail beschermd]} Gebruiker {id = 3, naam = Jennifer, [e-mail beschermd]} Gebruiker {id = 4 , name = Helen, [email protected]} Gebruiker {id = 5, name = Rachel, [email protected]}

3. De hoekige toepassing

Nu onze demo Spring Boot-applicatie actief is, gaan we nu een eenvoudige Angular-applicatie maken die de REST-controller-API kan gebruiken.

3.1. Hoekige CLI-installatie

We gebruiken Angular CLI, een krachtig opdrachtregelprogramma, om onze Angular-applicatie te maken.

Angular CLI is sindsdien een uiterst waardevol hulpmiddel het stelt ons in staat om vanuit het niets een volledig Angular-project te creëren en componenten, services, klassen en interfaces te genereren met slechts een paar opdrachten.

Zodra we npm (Node Package Manager) hebben geïnstalleerd, openen we een opdrachtconsole en typen we de opdracht:

npm install -g @ angular / [e-mail beveiligd]

Dat is het. Met de bovenstaande opdracht wordt de nieuwste versie van Angular CLI geïnstalleerd.

3.2. Projectsteigers met Angular CLI

In feite kunnen we onze Angular-applicatiestructuur vanaf de basis genereren. Maar eerlijk gezegd is dit een foutgevoelige en tijdrovende taak die we in alle gevallen moeten vermijden.

In plaats daarvan laten we Angular CLI het harde werk voor ons doen. Laten we dus een opdrachtconsole openen, vervolgens naar de map navigeren waarin we onze applicatie willen maken en de opdracht typen:

ng nieuwe angularclient

De nieuw commando genereert de volledige applicatiestructuur binnen het angularclient directory.

3.3. Het beginpunt van de hoekapplicatie

Als we binnen kijken angularclient map, zullen we zien dat Angular CLI effectief een heel project voor ons heeft gemaakt.

De applicatiebestanden van Angular gebruiken TypeScript, een getypte superset van JavaScript die compileert naar gewoon JavaScript. Het toegangspunt van elke Angular-applicatie is echter eenvoudig oud index.html het dossier.

Laten we dit bestand als volgt bewerken:

    Spring Boot - Hoekige toepassing 

Zoals we hierboven kunnen zien, hebben we Bootstrap 4 toegevoegd, zodat we onze applicatie-UI-componenten een meer chique uitstraling kunnen geven. Het is natuurlijk mogelijk om een ​​andere UI-kit op te halen uit het beschikbare stel.

Let op de gewoonte tags in de sectie. Op het eerste gezicht zien ze er nogal raar uit, zoals is geen standaard HTML 5-element.

Laten we ze daar houden, zoals is de root-selector die Angular gebruikt voor het weergeven van de root-component van de applicatie.

3.4. De app.component.ts Root-component

Om beter te begrijpen hoe Angular een HTML-sjabloon aan een component bindt, gaan we naar het src / app directory en bewerk het app.component.ts TypeScript-bestand - de root-component:

importeer {Component} vanuit '@ angular / core'; @Component ({selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']}) exportklasse AppComponent {title: string; constructor () {this.title = 'Spring Boot - Angular Application'; }}

Om voor de hand liggende redenen zullen we niet diep ingaan op het leren van TypeScript. Laten we echter opmerken dat het bestand een AppComponent class, die een veld declareert titel van het type draad (kleine behuizing). Absoluut, het is getypt JavaScript.

Bovendien initialiseert de constructor het veld met een draad waarde, wat vrij gelijkaardig is aan wat we doen in Java.

Het meest relevante deel is de @Component metadata marker of decorateur, dat drie elementen definieert:

  1. selector - de HTML-selector die wordt gebruikt om de component aan het HTML-sjabloonbestand te binden
  2. templateUrl - het HTML-sjabloonbestand dat aan de component is gekoppeld
  3. styleUrls - een of meer CSS-bestanden die aan de component zijn gekoppeld

Zoals verwacht kunnen we de app.component.html en app.component.css bestanden om de HTML-sjabloon en de CSS-stijlen van de rootcomponent te definiëren.

eindelijk, de selector element bindt de hele component aan het selector opgenomen in de index.html het dossier.

3.5. De app.component.html het dossier

Sinds de app.component.html bestand stelt ons in staat om definieer de HTML-sjabloon van de rootcomponent - de AppComponent class - we zullen het gebruiken om een ​​basisnavigatiebalk met twee knoppen te maken.

Als we op de eerste knop klikken, toont Angular een tabel met de lijst met Gebruiker entiteiten die zijn opgeslagen in de database. Evenzo, als we op de tweede klikken, wordt er een HTML-formulier weergegeven, dat we kunnen gebruiken om nieuwe entiteiten aan de database toe te voegen:

{{title}}

  • Lijst gebruikers
  • Voeg gebruiker toe

Het grootste deel van het bestand is standaard HTML, met een paar kanttekeningen die het vermelden waard zijn.

De eerste is de {{title}} uitdrukking. De dubbele accolades {{variabele-naam}} is de tijdelijke aanduiding die Angular gebruikt voor het uitvoeren van variabele interpolatie.

Laten we in gedachten houden dat de AppComponent class geïnitialiseerd het titel veld met de waarde Spring Boot - Hoekige toepassing. Angular geeft dus de waarde van dit veld in de sjabloon weer. Evenzo wordt het wijzigen van de waarde in de constructor weerspiegeld in de sjabloon.

Het tweede om op te merken is de routerLink attribuut.

Angular gebruikt dit kenmerk voor het routeren van verzoeken via zijn routeringsmodule (hierover later meer). Voorlopig is het voldoende om te weten dat de module een verzoek naar het / gebruikers pad naar een specifiek onderdeel en een verzoek naar /Voeg gebruiker toe naar een ander onderdeel.

In elk geval wordt de HTML-sjabloon die aan de overeenkomende component is gekoppeld, weergegeven in de tijdelijke aanduiding.

3.6. De Gebruiker Klasse

Omdat onze Angular-applicatie zal ophalen van en zal blijven bestaan Gebruiker entiteiten in de database, laten we een eenvoudig domeinmodel implementeren met TypeScript.

Laten we een terminalconsole openen en een model- directory:

ng klassengebruiker genereren

Angular CLI genereert een leeg Gebruiker klasse. Laten we het vullen met een paar velden:

exportklasse Gebruiker {id: string; naam: string; e-mail: tekenreeks; }

3.7. De Gebruikersservice Onderhoud

Met ons client-side domein Gebruiker klasse al ingesteld, laten we nu een serviceklasse implementeren die GET- en POST-verzoeken uitvoert naar het // localhost: 8080 / gebruikers-eindpunt.

Hierdoor kunnen we de toegang tot de REST-controller inkapselen in een enkele klasse, die we in de hele applicatie kunnen hergebruiken.

Laten we een consoleterminal openen en vervolgens een onderhoud directory en geef binnen die directory de volgende opdracht:

ng service user-service genereren

Laten we nu het gebruiker.service.ts bestand dat Angular CLI zojuist heeft gemaakt en herstructureer het:

importeer {Injectable} vanuit '@ angular / core'; importeer {HttpClient, HttpHeaders} vanuit '@ angular / common / http'; importeer {User} uit '../model/user'; importeer {Observable} vanuit 'rxjs / Observable'; @Injectable () exportklasse UserService {private usersUrl: string; constructor (privé http: HttpClient) {this.usersUrl = '// localhost: 8080 / gebruikers'; } openbare findAll (): Observable {retourneer this.http.get (this.usersUrl); } openbaar opslaan (gebruiker: gebruiker) {retourneer this.http.post (this.usersUrl, gebruiker); }}

We hebben geen solide achtergrond voor TypeScript nodig om te begrijpen hoe het Gebruikersservice klasse werkt. Simpel gezegd, het wordt ingekapseld in een herbruikbaar onderdeel alle functionaliteit die nodig is om de REST-controller-API te gebruiken die we eerder hebben geïmplementeerd in Spring Boot.

De vind alle() methode voert een GET HTTP-verzoek uit naar het // localhost: 8080 / gebruikers-eindpunt via Angular's HttpClient. De methode retourneert een Waarneembaar instantie die een array van Gebruiker voorwerpen.

Evenzo is het sparen() methode voert een POST HTTP-verzoek uit naar het // localhost: 8080 / users eindpunt.

Door het type te specificeren Gebruiker in de HttpClientDankzij de verzoekmethoden kunnen we back-endantwoorden op een eenvoudigere en effectievere manier consumeren.

Laten we tot slot let op het gebruik van de @Injectable () metagegevensmarkering. Dit geeft aan dat de service moet worden gemaakt en geïnjecteerd via de afhankelijkheidsinjectoren van Angular.

3.8. De UserListComponent Component

In dit geval is het Gebruikersservice class is de dunne middelste laag tussen de REST-service en de presentatielaag van de applicatie. Daarom moeten we een component definiëren die verantwoordelijk is voor het weergeven van de lijst met Gebruiker entiteiten bleven bestaan ​​in de database.

Laten we een terminalconsole openen en vervolgens een gebruikers lijst directory en genereer een gebruikerslijstcomponent:

ng component gebruikerslijst genereren

Angular CLI genereert een lege componentklasse die de ngOnInit koppel. De interface verklaart een hook ngOnInit () method, die Angular aanroept nadat het klaar is met het instantiëren van de implementatieklasse, en ook na het aanroepen van de constructor.

Laten we de klas refactoren zodat er een Gebruikersservice instantie in de constructor:

importeer {Component, OnInit} vanuit '@ angular / core'; importeer {User} uit '../model/user'; importeer {UserService} uit '../service/user.service'; @Component ({selector: 'app-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css']}) exportklasse UserListComponent implementeert OnInit {gebruikers: gebruiker []; constructor (privé userService: UserService) {} ngOnInit () {this.userService.findAll (). subscribe (data => {this.users = data;}); }} 

De implementatie van het UserListComponent klasse spreekt voor zich. Het gebruikt gewoon de FindAll () van UserService methode om alle entiteiten op te halen die in de database zijn opgeslagen en deze op te slaan in het gebruikers veld.

Bovendien moeten we het HTML-bestand van de component bewerken, user-list.component.html, om de tabel te maken die de lijst met entiteiten weergeeft:

#NaamE-mail
{{ gebruikersnaam }}{{user.name}}{{user.email}}

Let op het gebruik van de * ngFor richtlijn. De richtlijn heet een repeater, en we kunnen het gebruiken om de inhoud van een variabele te herhalen en HTML-elementen iteratief weer te geven. In dit geval hebben we het gebruikt om de rijen van de tabel dynamisch weer te geven.

Bovendien hebben we variabele interpolatie gebruikt om de ID kaart,naam, en e-mail van elke gebruiker.

3.9. De UserFormComponent Component

Evenzo moeten we een component maken waarmee we een nieuwe kunnen voortzetten Gebruiker object in de database.

Laten we een gebruikersformulier directory en typ het volgende:

ng component gebruikersformulier genereren 

Laten we vervolgens het user-form.component.ts bestand en voeg toe aan het UserFormComponent class a methode voor het opslaan van een Gebruiker voorwerp:

importeer {Component} vanuit '@ angular / core'; importeer {ActivatedRoute, Router} vanuit '@ angular / router'; importeer {UserService} uit '../service/user.service'; importeer {User} uit '../model/user'; @Component ({selector: 'app-user-form', templateUrl: './user-form.component.html', styleUrls: ['./user-form.component.css']}) exportklasse UserFormComponent {gebruiker : Gebruiker; constructor (privéroute: ActivatedRoute, privérouter: Router, privé userService: UserService) {this.user = nieuwe gebruiker (); } onSubmit () {this.userService.save (this.user) .subscribe (resultaat => this.gotoUserList ()); } gotoUserList () {this.router.navigate (['/ gebruikers']); }}

In dit geval, UserFormComponent neemt ook een Gebruikersservice instantie in de constructor, die de onSubmit () methode gebruikt voor het opslaan van de meegeleverde Gebruiker voorwerp.

Omdat we de bijgewerkte lijst met entiteiten opnieuw moeten weergeven zodra we een nieuwe hebben behouden, noemen we de gotoUserList () methode na de invoeging, die de gebruiker omleidt naar de / gebruikers pad.

Bovendien moeten we het user-form.component.html bestand en maak het HTML-formulier om een ​​nieuwe gebruiker in de database te behouden:

 Naam Naam is vereist E-mail E-mail is vereist Verzenden 

Op het eerste gezicht ziet de vorm er vrij standaard uit. Maar het omvat veel van de functionaliteit van Angular achter de schermen.

Laten we eens kijken naar het gebruik van de ngSubmit richtlijn, die de onSubmit () methode wanneer het formulier wordt verzonden.

Vervolgens hebben we de template variabele #gebruikerForm, dus Angular voegt automatisch een NgForm richtlijn, waardoor we het formulier als geheel kunnen volgen.

De NgForm richtlijn bevat de besturingselementen die we hebben gemaakt voor de formulierelementen met een ngModel richtlijn en een naam attribuut en bewaakt ook hun eigenschappen, inclusief hun staat.

De ngModel richtlijn geeft ons een bidirectionele gegevensbindingfunctionaliteit tussen de formulierbesturingselementen en het client-side domeinmodel - het Gebruiker klasse.

Dit betekent dat gegevens die in de formulierinvoervelden zijn ingevoerd, naar het model zullen stromen - en andersom. Wijzigingen in beide elementen worden onmiddellijk weergegeven via DOM-manipulatie.

Bovendien, ngModel stelt ons in staat om de status van elk formuliercontrole bij te houden en client-side validatie uit te voeren, door aan elk besturingselement verschillende CSS-klassen en DOM-eigenschappen toe te voegen.

In het bovenstaande HTML-bestand hebben we de eigenschappen die zijn toegepast op de formulierbesturingselementen alleen gebruikt om een ​​waarschuwingsvenster weer te geven wanneer de waarden in het formulier zijn gewijzigd.

3.10. De app-routing.module.ts het dossier

Hoewel de componenten afzonderlijk functioneel zijn, moeten we nog steeds een mechanisme gebruiken om ze op te roepen wanneer de gebruiker op de knoppen in de navigatiebalk klikt.

Dit is waar de RouterModule komt in het spel. Dus laten we het app-routing.module.ts bestand en configureer de module, zodat deze verzoeken naar de overeenkomende componenten kan verzenden:

importeer {NgModule} vanuit '@ angular / core'; importeer {Routes, RouterModule} vanuit '@ angular / router'; importeer {UserListComponent} uit './user-list/user-list.component'; importeer {UserFormComponent} uit './user-form/user-form.component'; const routes: Routes = [{pad: 'gebruikers', component: UserListComponent}, {pad: 'adduser', component: UserFormComponent}]; @NgModule ({invoer: [RouterModule.forRoot (routes)], export: [RouterModule]}) exportklasse AppRoutingModule {} 

Zoals we hierboven kunnen zien, de Routes array instrueert de router welke component moet worden weergegeven wanneer een gebruiker op een link klikt of specificeert een URL in de adresbalk van de browser.

Een route bestaat uit twee delen:

  1. Pad - een draad die overeenkomt met de URL in de adresbalk van de browser
  2. Component - de component die moet worden gemaakt wanneer de route actief is (genavigeerd)

Als de gebruiker op het Lijst gebruikers knop, die linkt naar de / gebruikers pad, of voert de URL in de adresbalk van de browser in, zal de router het UserListComponent component sjabloonbestand in het tijdelijke aanduiding.

Evenzo, als ze op het Voeg gebruiker toe knop, zal het de UserFormComponent component.

3.11. De app.module.ts het dossier

Vervolgens moeten we het app.module.ts bestand, zodat Angular alle vereiste modules, componenten en services kan importeren.

Bovendien moeten we specificeren welke provider we zullen gebruiken voor het maken en injecteren van het Gebruikersservice klasse. Anders kan Angular het niet in de componentklassen injecteren:

importeer {BrowserModule} vanuit '@ angular / platform-browser'; importeer {NgModule} vanuit '@ angular / core'; importeer {AppRoutingModule} vanuit './app-routing.module'; importeer {FormsModule} vanuit '@ angular / formulieren'; importeer {HttpClientModule} vanuit '@ angular / common / http'; importeer {AppComponent} van './app.component'; importeer {UserListComponent} uit './user-list/user-list.component'; importeer {UserFormComponent} uit './user-form/user-form.component'; importeer {UserService} vanuit './service/user.service'; @NgModule ({declaraties: [AppComponent, UserListComponent, UserFormComponent], import: [BrowserModule, AppRoutingModule, HttpClientModule, FormsModule], providers: [UserService], bootstrap: [AppComponent]}) exportklasse AppModule {}

4. Het uitvoeren van de applicatie

Eindelijk zijn we klaar om onze applicatie uit te voeren.

Om dit te bereiken, laten we eerst de Spring Boot-applicatie draaien, zodat de REST-service actief is en luistert naar verzoeken.

Zodra de Spring Boot-applicatie is gestart, openen we een opdrachtconsole en typen we de volgende opdracht:

ng serveren --open

Hiermee wordt de live-ontwikkelingsserver van Angular gestart en wordt ook de browser geopend op // localhost: 4200.

We zouden de navigatiebalk moeten zien met de knoppen voor het weergeven van bestaande entiteiten en voor het toevoegen van nieuwe. Als we op de eerste knop klikken, zouden we onder de navigatiebalk een tabel moeten zien met de lijst van entiteiten die in de database aanwezig zijn:

Evenzo zal het klikken op de tweede knop het HTML-formulier weergeven voor het behouden van een nieuwe entiteit:

5. Conclusie

In deze tutorial we leerden hoe we een eenvoudige webapplicatie konden bouwen met Spring Boot en Angular.

Zoals gewoonlijk zijn alle codevoorbeelden die in deze tutorial worden getoond, beschikbaar op GitHub.