Articles

Bouw een Basis CRUD App met Angular en Node

Posted on

De laatste jaren zijn single page applications (SPA’s) steeds populairder geworden. Een SPA is een website die uit slechts één pagina bestaat. Die ene pagina fungeert als een container voor een JavaScript applicatie. Het JavaScript is verantwoordelijk voor het verkrijgen van de inhoud en het renderen ervan binnen de container. De inhoud wordt meestal verkregen van een webservice en RESTful API’s zijn in veel situaties de eerste keuze geworden. Het deel van de toepassing dat de SPA vormt, staat algemeen bekend als de client of front-end, terwijl het deel dat verantwoordelijk is voor de REST API bekend staat als de server of back-end. In deze tutorial ontwikkel je een eenvoudige Angular single page app met een REST backend, gebaseerd op Node en Express.

Je gebruikt Angular omdat het het MVC pattern volgt en op een nette manier de View van de Models scheidt. Het is eenvoudig om HTML templates te maken die dynamisch worden gevuld met gegevens en automatisch worden bijgewerkt wanneer de gegevens veranderen. Ik ben van dit framework gaan houden omdat het erg krachtig is, een enorme gemeenschap heeft en uitstekende documentatie.

Voor de server zul je Node met Express gebruiken. Express is een framework dat het makkelijk maakt om REST API’s te maken door het mogelijk te maken om code te definiëren die voor verschillende verzoeken op de server draait. Extra diensten kunnen globaal worden toegevoegd, of afhankelijk van het verzoek. Er zijn een aantal frameworks die bovenop Express bouwen en de taak automatiseren om je database modellen in een API om te zetten. Deze tutorial maakt hier geen gebruik van om de focus te behouden.

Angular moedigt het gebruik van TypeScript aan. TypeScript voegt type-informatie toe aan JavaScript en is, naar mijn mening, de toekomst van het ontwikkelen van grootschalige toepassingen in JavaScript. Om deze reden zul je zowel de client als de server ontwikkelen met TypeScript.

Hier volgen de bibliotheken die je zult gebruiken voor de client en de server:

  • Angular: Het framework dat gebruikt wordt om de client applicatie te bouwen
  • Okta voor Autorisatie: Een plugin die single sign-on autorisatie beheert met behulp van Okta, zowel op de client als op de server
  • Angular Material: Een angular plugin die out-of-the-box Material Design biedt
  • Node: De eigenlijke server waarop de JavaScript-code wordt uitgevoerd
  • Express: Een routing library voor het reageren op serververzoeken en het bouwen van REST API’s
  • TypeORM: Een database ORM bibliotheek voor TypeScript

Start je basis Angular Client Applicatie

Laten we beginnen met het implementeren van een basis client met Angular. Het doel is om een product catalogus te ontwikkelen waarmee je producten, hun prijzen, en hun voorraadniveau kunt beheren. Aan het eind van dit hoofdstuk heb je een eenvoudige applicatie bestaande uit een bovenbalk en twee views, Home en Products. De Producten-weergave zal nog geen inhoud hebben en niets zal met een wachtwoord worden beschermd. Dit zal worden behandeld in de volgende secties.

Om te beginnen moet u Angular installeren. Ik ga ervan uit dat u Node al op uw systeem heeft geïnstalleerd en u kunt het npm commando gebruiken. Typ het volgende commando in een terminal.

npm install -g @angular/[email protected]

Afhankelijk van je systeem, moet je dit commando misschien uitvoeren met sudo omdat het pakket dan globaal wordt geïnstalleerd. Het angular-cli pakket levert het ng commando dat wordt gebruikt om Angular applicaties te beheren. Ga na de installatie naar een map naar keuze en maak uw eerste Angular-toepassing met het volgende commando.

ng new MyAngularClient

Als u Angular 7 gebruikt, krijgt u twee vragen. De eerste vraagt je of je routing wilt opnemen. Antwoord hierop met ja. De tweede vraag gaat over het type stylesheets dat u wilt gebruiken. Laat dit op de standaard CSS staan.

ng new zal een nieuwe directory aanmaken genaamd MyAngularClient en deze vullen met een applicatie-skelet. Laten we even de tijd nemen om enkele van de bestanden te bekijken die het vorige commando heeft aangemaakt. In de src directory van de app, vindt u een bestand index.html dat de hoofdpagina is van de toepassing. Het bevat niet veel en speelt gewoon de rol van een container. U zult ook een style.css bestand zien. Dit bevat het globale stijlblad dat in de hele toepassing wordt toegepast. Als u door de mappen bladert, ziet u misschien een directory src/app die vijf bestanden bevat.

app-routing.module.tsapp.component.cssapp.component.htmlapp.component.tsapp.component.spec.tsapp.module.ts

Deze bestanden definiëren de belangrijkste toepassingscomponent die in de index.html zal worden ingevoegd. Hier volgt een korte beschrijving van elk van de bestanden:

  • app.component.css bestand bevat de stylesheets van de belangrijkste app component. Stijlen kunnen lokaal voor elke component worden gedefinieerd
  • app.component.html bevat het HTML-sjabloon van de component
  • app.component.ts bestand bevat de code die de view regelt
  • app.module.ts definieert welke modules uw app zal gebruiken
  • app-routing.module.ts is opgezet om de routes voor je applicatie te definiëren
  • app.component.spec.ts bevat een skelet voor unit testing van de app component

Ik zal het testen in deze tutorial niet behandelen, maar in het echte leven zou je gebruik moeten maken van deze functie. Voordat je aan de slag kunt, moet je nog een paar pakketten installeren. Deze zullen u helpen om snel een mooi ontworpen responsive layout te maken. Navigeer naar de basismap van de client, MyAngularClient, en type het volgende commando.

De @angular/material en @angular/cdk bibliotheken bieden componenten die zijn gebaseerd op Google’s Material Design, @angular/animations wordt gebruikt om vloeiende overgangen te bieden, en @angular/flex-layout geeft je de tools om je ontwerp responsive te maken.

Volgende, maak het HTML-sjabloon voor de app component. Open src/app/app.component.html en vervang de inhoud door het volgende.

<mat-toolbar color="primary" class="expanded-toolbar"> <button mat-button routerLink="/">{{title}}</button> <div fxLayout="row" fxShow="false" fxShow.gt-sm> <button mat-button routerLink="/"><mat-icon>home</mat-icon></button> <button mat-button routerLink="/products">Products</button> <button mat-button *ngIf="!isAuthenticated" (click)="login()"> Login </button> <button mat-button *ngIf="isAuthenticated" (click)="logout()"> Logout </button> </div> <button mat-button ="menu" fxHide="false" fxHide.gt-sm> <mat-icon>menu</mat-icon> </button></mat-toolbar><mat-menu x-position="before" #menu="matMenu"> <button mat-menu-item routerLink="/"><mat-icon>home</mat-icon> Home</button> <button mat-menu-item routerLink="/products">Products</button>; <button mat-menu-item *ngIf="!isAuthenticated" (click)="login()"> Login </button> <button mat-menu-item *ngIf="isAuthenticated" (click)="logout()"> Logout </button></mat-menu><router-outlet></router-outlet>

De mat-toolbar bevat de werkbalk voor materiaalontwerp, terwijl router-outlet de container is die door de router zal worden gevuld. Het bestand app.component.ts moet worden bewerkt zodat het het volgende bevat.

import { Component } from '@angular/core';@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: })export class AppComponent { public title = 'My Angular App'; public isAuthenticated: boolean; constructor() { this.isAuthenticated = false; } login() { } logout() { }}

Dit is de controller voor het app component. U kunt zien dat het een eigenschap bevat genaamd isAuthenticated samen met twee methoden login en logout. Op dit moment doen deze nog niets. Ze zullen worden geïmplementeerd in de volgende sectie die gebruikersauthenticatie met Okta behandelt. Definieer nu alle modules die je gaat gebruiken. Vervang de inhoud van app.module.ts door de onderstaande code:

import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { BrowserAnimationsModule } from '@angular/platform-browser/animations';import { FlexLayoutModule } from '@angular/flex-layout';import { MatButtonModule, MatDividerModule, MatIconModule, MatMenuModule, MatProgressSpinnerModule, MatTableModule, MatToolbarModule} from '@angular/material';import { HttpClientModule } from '@angular/common/http';import { FormsModule } from '@angular/forms';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';@NgModule({ declarations: , imports: , providers: , bootstrap: })export class AppModule { }

Let op alle material design-modules. De @angular/material bibliotheek vereist dat u een module importeert voor elk type component dat u in uw app wilt gebruiken. Vanaf Angular 7 bevat het standaard applicatie skelet een apart bestand genaamd app-routing.module.ts. Bewerk dit bestand om de volgende routes aan te geven.

import { NgModule } from '@angular/core';import { Routes, RouterModule } from '@angular/router';import { ProductsComponent } from './products/products.component';import { HomeComponent } from './home/home.component';const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }

Dit definieert twee routes die corresponderen met het root-pad en met het products-pad. Het koppelt ook de HomeComponent en de ProductsComponent aan deze routes. Maak deze componenten nu. Typ in de basismap van de Angular client de volgende commando’s.

ng generate component Productsng generate component Home

Dit creëert htmlcssts, en spec.ts bestanden voor elk onderdeel. Het werkt ook app.module.ts bij om de nieuwe componenten aan te geven. Open home.component.html in de src/app/home directory en plak de volgende inhoud.

<div class="hero"> <div> <h1>Hello World</h1> <p class="lead">This is the homepage of your Angular app</p> </div></div>

Breng ook wat styling aan in het home.component.css bestand.

.hero { text-align: center; height: 90vh; display: flex; flex-direction: column; justify-content: center; font-family: sans-serif;}

Laat de ProductsComponent voor nu leeg. Dit zal worden geïmplementeerd zodra u de back-end REST-server hebt gemaakt en in staat bent om het te vullen met een aantal gegevens. Om alles er mooi uit te laten zien blijven er slechts twee kleine taken over. Kopieer de volgende stijlen in src/style.css

@import "~@angular/material/prebuilt-themes/deeppurple-amber.css";body { margin: 0; font-family: sans-serif;}.expanded-toolbar { justify-content: space-between;}h1 { text-align: center;}

Finishing, om de Material Design-pictogrammen te renderen, voegt u één regel toe aan de <head>-tags van het index.html-bestand.

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

U bent nu klaar om de Angular server op te starten en te zien wat u tot nu toe hebt bereikt. Typ in de basisdirectory van de client-app het volgende commando.

ng serve

Open vervolgens uw browser en navigeer naar http://localhost:4200.

Voeg Authenticatie toe aan uw Node + Angular App

Als u ooit webapplicaties vanaf nul heeft ontwikkeld, weet u hoeveel werk het is om gebruikers zich te laten registreren, verifiëren, aanmelden en afmelden bij uw applicatie. Met behulp van Okta kan dit proces sterk worden vereenvoudigd. Om te beginnen hebt u een ontwikkelaarsaccount bij Okta nodig.

developer.okta.com

In uw browser navigeert u naar developer.okta.com en klik op Create Free Account en voer uw gegevens in.

Start met bouwen op Okta

Als u klaar bent, komt u op uw dashboard voor ontwikkelaars. Klik op de knop Applicatie toevoegen om een nieuwe applicatie te maken.

Add Application

Start met het maken van een nieuwe applicatie met één pagina. Kies Single Page App en klik op Next.

Create new Single Page App

Op de volgende pagina moet u de standaardinstellingen bewerken. Zorg ervoor dat het poortnummer 4200 is. Dit is de standaardpoort voor Angular-toepassingen.

Mijn Angular-app

Dat is alles. U zou nu een Client ID moeten zien die u in uw TypeScript-code moet plakken.

Om authenticatie in de client te implementeren, installeert u de Okta-bibliotheek voor Angular.

npm install @okta/[email protected] --save-exact

In app.module.ts importeer je de OktaAuthModule.

import { OktaAuthModule } from '@okta/okta-angular';

In de lijst van imports van de app module, voeg toe:

OktaAuthModule.initAuth({ issuer: 'https://{yourOktaDomain}/oauth2/default', redirectUri: 'http://localhost:4200/implicit/callback', clientId: '{YourClientId}'})

Hier yourOktaDomain moet worden vervangen door het ontwikkelingsdomein dat u in uw browser ziet wanneer u naar uw Okta-dashboard navigeert. YourClientId moet worden vervangen door de client-ID die u hebt verkregen bij het registreren van uw applicatie. De bovenstaande code maakt de Okta Authenticatie Module beschikbaar in uw applicatie. Gebruik deze in app.component.ts, en importeer de service.

import { OktaAuthService } from '@okta/okta-angular';

Modificeer de constructor om de service te injecteren en u erop te abonneren.

constructor(public oktaAuth: OktaAuthService) { this.oktaAuth.$authenticationState.subscribe( (isAuthenticated: boolean) => this.isAuthenticated = isAuthenticated );}

Nu worden alle wijzigingen in de authenticatiestatus weergegeven in de isAuthenticated eigenschap. U moet deze nog initialiseren wanneer de component wordt geladen. Maak een ngOnInit methode en voeg implements OnInit aan uw klasse definitie toe

import { Component, OnInit } from '@angular/core';...export class AppComponent implements OnInit { ... async ngOnInit() { this.isAuthenticated = await this.oktaAuth.isAuthenticated(); }}

Eindig, implementeer je de login en logout methode om op de gebruikersinterface te reageren en de gebruiker aan of af te melden.

login() { this.oktaAuth.loginRedirect();}logout() { this.oktaAuth.logout('/');}

In de routingmodule moet u de route registreren die zal worden gebruikt voor het inlogverzoek. Open app-routing.module.ts en importeer OktaCallbackComponent en OktaAuthGuard.

import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';

Voeg nog een route toe aan de routes array.

{ path: 'implicit/callback', component: OktaCallbackComponent}

Dit stelt de gebruiker in staat om in te loggen met de knop Aanmelden. Om de Products-route te beschermen tegen onbevoegde toegang, voegt u de volgende regel toe aan de products-route.

{ path: 'products', component: ProductsComponent, canActivate: }

Dat is alles wat er over te zeggen valt. Wanneer een gebruiker nu toegang probeert te krijgen tot de Producten-weergave, wordt hij doorgestuurd naar de Okta-inlogpagina. Eenmaal ingelogd, wordt de gebruiker teruggeleid naar de Producten-weergave.

Elementeer een Node REST API

De volgende stap is het implementeren van een server gebaseerd op Node en Express die productinformatie zal opslaan. Dit zal gebruik maken van een aantal kleinere bibliotheken om je leven gemakkelijker te maken. Om in TypeScript te ontwikkelen, hebt u typescript en tsc nodig. Voor de database-abstractielaag maak je gebruik van TypeORM. Dit is een handige bibliotheek die gedrag injecteert in TypeScript-klassen en deze omzet in databasemodellen. Maak een nieuwe map voor je serverapplicatie en voer daarin het volgende commando uit.

npm init

Beantwoord alle vragen en voer vervolgens uit:

Ik zal niet al deze bibliotheken in detail behandelen, maar je zult zien dat @okta/jwt-verifier wordt gebruikt om JSON-webtokens te verifiëren en te authenticeren.

Om TypeScript te laten werken, maakt u een bestand tsconfig.json en plakt u daarin de volgende inhoud.

{ "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "dist", "experimentalDecorators": true, "emitDecoratorMetadata": true }, "include": , "exclude": }

De eerste stap bij het maken van de server is het maken van een databasemodel voor het product. Met TypeORM is dit heel eenvoudig. Maak een subdirectory src en maak daarbinnen een bestand model.ts. Plak de volgende inhoud.

import {Entity, PrimaryGeneratedColumn, Column, createConnection, Connection, Repository} from 'typeorm';@Entity()export class Product { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() sku: string; @Column('text') description: string; @Column() price: number; @Column() stock: number;}

De TypeORM-annotaties zetten de klassendefinitie om in een databasemodel. Ik ben dol op het TypeORM-project vanwege het gebruiksgemak en de grote verscheidenheid aan ondersteunde SQL- en NoSQL-databasekoppelingen. Ik stel voor dat je de documentatie bekijkt op https://github.com/typeorm/typeorm.

Je zult ook toegang moeten krijgen tot een repository van het product. Voeg ook in het model.ts bestand het volgende toe.

let connection:Connection;export async function getProductRepository(): Promise<Repository<Product>> { if (connection===undefined) { connection = await createConnection({ type: 'sqlite', database: 'myangularapp', synchronize: true, entities: , }); } return connection.getRepository(Product);}

Merk op dat hier voor de eenvoud SQLite wordt gebruikt. In een real-world scenario moet u dit vervangen door een databaseconnector naar keuze.

Naast maakt u een bestand met de naam product.ts. Dit bestand bevat de logica voor alle routes voor de CRUD-bewerkingen op producten.

import { NextFunction, Request, Response, Router } from 'express';import { getProductRepository, Product } from './model';export const router: Router = Router();router.get('/product', async function (req: Request, res: Response, next: NextFunction) { try { const repository = await getProductRepository(); const allProducts = await repository.find(); res.send(allProducts); } catch (err) { return next(err); }});router.get('/product/:id', async function (req: Request, res: Response, next: NextFunction) { try { const repository = await getProductRepository(); const product = await repository.find({id: req.params.id}); res.send(product); } catch (err) { return next(err); }});router.post('/product', async function (req: Request, res: Response, next: NextFunction) { try { const repository = await getProductRepository(); const product = new Product(); product.name = req.body.name; product.sku = req.body.sku; product.description = req.body.description; product.price = Number.parseFloat(req.body.price); product.stock = Number.parseInt(req.body.stock); const result = await repository.save(product); res.send(result); } catch (err) { return next(err); }});router.post('/product/:id', async function (req: Request, res: Response, next: NextFunction) { try { const repository = await getProductRepository(); const product = await repository.findOne({id: req.params.id}); product.name = req.body.name; product.sku = req.body.sku; product.description = req.body.description; product.price = Number.parseFloat(req.body.price); product.stock = Number.parseInt(req.body.stock); const result = await repository.save(product); res.send(result); } catch (err) { return next(err); }});router.delete('/product/:id', async function (req: Request, res: Response, next: NextFunction) { try { const repository = await getProductRepository(); await repository.delete({id: req.params.id}); res.send('OK'); } catch (err) { return next(err); }});

Dit bestand is enigszins lang, maar bevat niets verrassends. Product objecten worden aangemaakt en opgeslagen in of verwijderd uit de database.

Laten we onze aandacht weer richten op de authenticatie. Je wilt er zeker van zijn dat alleen geauthenticeerde gebruikers toegang hebben tot de service. Maak een bestand aan met de naam auth.ts en plak daarin het volgende.

import { Request, Response, NextFunction} from 'express';const OktaJwtVerifier = require('@okta/jwt-verifier');const oktaJwtVerifier = new OktaJwtVerifier({ clientId: '{YourClientId}', issuer: 'https://{yourOktaDomain}/oauth2/default'});export async function oktaAuth(req:Request, res:Response, next:NextFunction) { try { const token = (req as any).token; if (!token) { return res.status(401).send('Not Authorised'); } const jwt = await oktaJwtVerifier.verifyAccessToken(token); req.user = { uid: jwt.claims.uid, email: jwt.claims.sub }; next(); } catch (err) { return res.status(401).send(err.message); }}

Net als in de clienttoepassing moet yourOktaDomain worden vervangen door het ontwikkelingsdomein en YourClientId moet worden vervangen door de client-ID van uw toepassing. De oktaJwtVerifier instantie neemt een JWT token en verifieert het. Indien succesvol, worden de gebruikers-id en e-mail opgeslagen in req.user. Zo niet, dan zal de server antwoorden met een 401 status code. Het laatste stuk om de server te vervolledigen is het hoofdingangspunt dat de server start en de middleware registreert die je tot nu toe gedefinieerd hebt. Maak een bestand server.ts met de volgende inhoud.

import * as express from 'express';import * as cors from 'cors';import * as bodyParser from 'body-parser';const bearerToken = require('express-bearer-token');import {router as productRouter} from './product'import {oktaAuth} from './auth'const app = express() .use(cors()) .use(bodyParser.json()) .use(bearerToken()) .use(oktaAuth) .use(productRouter);app.listen(4201, (err) => { if (err) { return console.log(err); } return console.log('My Node App listening on port 4201');});

Om de TypeScript te compileren, voert u het commando uit

npx tsc

Dan, als u de server wilt starten, voert u gewoon uit:

node dist/server.js

Voltooi uw Angular-client

Nu de server is voltooid, gaan we de client afmaken. De eerste stap is het creëren van een klasse die de productgegevens bevat. Deze class is vergelijkbaar met de Product class in de server applicatie maar dan zonder de TypeORM annotaties. Het wordt opgenomen in een bestand met de naam product.ts.

export class Product { id?: string; name: string; sku: string; description: string; price: number; stock: number;}

Bewaar dit bestand in dezelfde directory als de products component. Het is het beste om de toegang tot de REST API in een aparte service in te kapselen. Maak een Products service aan door het onderstaande commando uit te voeren.

ng generate service products/Products

Dit zal een bestand aanmaken met de naam product.service.ts in de src/app/products directory. Vul het met de volgende inhoud.

import { Injectable } from '@angular/core';import { HttpClient } from '@angular/common/http';import { OktaAuthService } from '@okta/okta-angular';import { Product } from './product';const baseUrl = 'http://localhost:4201';@Injectable({ providedIn: 'root'})export class ProductsService { constructor(public oktaAuth: OktaAuthService, private http: HttpClient) { } private async request(method: string, url: string, data?: any) { const token = await this.oktaAuth.getAccessToken(); console.log('request ' + JSON.stringify(data)); const result = this.http.request(method, url, { body: data, responseType: 'json', observe: 'body', headers: { Authorization: `Bearer ${token}` } }); return new Promise<any>((resolve, reject) => { result.subscribe(resolve as any, reject as any); }); } getProducts() { return this.request('get', `${baseUrl}/product`); } getProduct(id: string) { return this.request('get', `${baseUrl}/product/${id}`); } createProduct(product: Product) { console.log('createProduct ' + JSON.stringify(product)); return this.request('post', `${baseUrl}/product`, product); } updateProduct(product: Product) { console.log('updateProduct ' + JSON.stringify(product)); return this.request('post', `${baseUrl}/product/${product.id}`, product); } deleteProduct(id: string) { return this.request('delete', `${baseUrl}/product/${id}`); }}

De ProductsService bevat een publieke methode voor elke route van de REST API. Het HTTP-verzoek wordt in een aparte methode ingekapseld. Merk op hoe het verzoek altijd een Bearer token bevat, verkregen uit de OktaAuthService. Dit is het token dat door de server wordt gebruikt om de gebruiker te authenticeren.

Nu kan de ProductsComponent worden geïmplementeerd. De volgende code is hiervoor geschikt.

import { Component, OnInit } from '@angular/core';import { MatTableDataSource } from '@angular/material';import { ProductsService } from './products.service';import { Product } from './product';@Component({ selector: 'app-products', templateUrl: './products.component.html', styleUrls: })export class ProductsComponent implements OnInit { displayedColumns: string = ; dataSource = new MatTableDataSource<any>(); selectedProduct: Product = new Product(); loading = false; constructor(public productService: ProductsService) { } ngOnInit() { this.refresh(); } async refresh() { this.loading = true; const data = await this.productService.getProducts(); this.dataSource.data = data; this.loading = false; } async updateProduct() { if (this.selectedProduct.id !== undefined) { await this.productService.updateProduct(this.selectedProduct); } else { await this.productService.createProduct(this.selectedProduct); } this.selectedProduct = new Product(); await this.refresh(); } editProduct(product: Product) { this.selectedProduct = product; } clearProduct() { this.selectedProduct = new Product(); } async deleteProduct(product: Product) { this.loading = true; if (confirm(`Are you sure you want to delete the product ${product.name}. This cannot be undone.`)) { this.productService.deleteProduct(product.id); } await this.refresh(); }}

De lay-out, in products.component.html, waarin het product wordt getoond, bestaat uit twee delen. Het eerste deel gebruikt een mat-table component om een lijst met producten weer te geven. Het tweede deel toont een formulier waarin de gebruiker een nieuw of bestaand product kan bewerken.

<h1 class="h1">Product Inventory</h1><div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between stretch" class="products"> <table mat-table fxFlex="100%" fxFlex.gt-sm="66%" ="dataSource" class="mat-elevation-z1"> <ng-container matColumnDef="name"> <th mat-header-cell *matHeaderCellDef> Name</th> <td mat-cell *matCellDef="let product"> {{product.name}}</td> </ng-container> <ng-container matColumnDef="sku"> <th mat-header-cell *matHeaderCellDef> SKU</th> <td mat-cell *matCellDef="let product"> {{product.sku}}</td> </ng-container> <ng-container matColumnDef="description"> <th mat-header-cell *matHeaderCellDef> Description</th> <td mat-cell *matCellDef="let product"> {{product.description}}</td> </ng-container> <ng-container matColumnDef="price"> <th mat-header-cell *matHeaderCellDef> Price</th> <td mat-cell *matCellDef="let product"> {{product.price}}</td> </ng-container> <ng-container matColumnDef="stock"> <th mat-header-cell *matHeaderCellDef> Stock Level</th> <td mat-cell *matCellDef="let product"> {{product.stock}}</td> </ng-container> <ng-container matColumnDef="edit"> <th mat-header-cell *matHeaderCellDef></th> <td mat-cell *matCellDef="let product"> <button mat-icon-button (click)="editProduct(product)"> <mat-icon>edit</mat-icon> </button> </td> </ng-container> <ng-container matColumnDef="delete"> <th mat-header-cell *matHeaderCellDef></th> <td mat-cell *matCellDef="let product"> <button mat-icon-button (click)="deleteProduct(product)"> <mat-icon>delete</mat-icon> </button> </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> <mat-divider fxShow="false" fxShow.gt-sm ="true"></mat-divider> <div> <h2>Selected Product</h2> <label>Name <input type="text" ="selectedProduct.name"> </label> <label>SKU <input type="text" ="selectedProduct.sku"> </label> <label>Description <input type="text" ="selectedProduct.description"> </label> <label>Price <input type="text" ="selectedProduct.price"> </label> <label>Stock Level <input type="text" ="selectedProduct.stock"> </label> <button mat-flat-button color="primary" (click)="updateProduct()">{{(selectedProduct.id!==undefined)?'Update':'Create'}}</button> <button mat-flat-button color="primary" (click)="clearProduct()">Clear</button> </div> <div class="loading" *ngIf="loading"> <mat-spinner></mat-spinner> </div></div>

Tot slot voegt u een beetje styling in products.component.css toe aan de lay-out.

.products { padding: 2rem;}label, input { display: block;}label { margin-bottom: 1rem;}.loading { position: absolute; display: flex; justify-content: center; align-content: center; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0.8);}

Wanneer alles klaar is, kunt u de client en de server opstarten en uw toepassing testen. Om het nog eens te herhalen, voert u in de directory met de server het volgende uit:

node dist/server.js

En in de directory met de client voert u het volgende uit:

ng serve

Uw applicatie zou er ongeveer als volgt uit moeten zien

Productinventaris

Lees meer over Angular, Node, en Express

In deze tutorial heb ik je door de ontwikkeling van een webapplicatie met één pagina geleid met behulp van Angular en Node. Met behulp van slechts een paar regels code was je in staat om gebruikersauthenticatie voor de client en de server te implementeren. Angular maakt gebruik van TypeScript, dat een superset is van de JavaScript-taal en type-informatie toevoegt. TypeScript zorgt voor stabielere code en daarom heb ik besloten om ook de Node/Express server met deze taal te implementeren. Als je nog niet bekend bent met TypeScript, bekijk dan deze geweldige introductie door Todd Motto. Hij heeft ook een aantal goede artikelen over Angular.

De volledige code van deze tutorial kun je vinden op GitHub.

Als je meer wilt leren over Angular, of Node/Express, hebben we nog een aantal andere bronnen voor je om te bekijken:

  • Simple Node Authentication
  • Build a Basic CRUD App with Node and React
  • Build a Simple API Service with Express and GraphQL
  • Angular 6 – What’s New and Why Upgrade?
  • Bouw een eenvoudige CRUD App met Angular 7 en Spring Boot

En zoals altijd, zouden we het leuk vinden als je ons volgt voor meer coole content en updates van ons team. Je kunt ons vinden op Twitter @oktadev, op Facebook, en LinkedIn.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *