So nutzen Sie die REST-API in Angular 15
Mit Angular sind alle Dienstprogramme zur Nutzung der REST-API einsatzbereit
Hallo Leute,
Heute erfahren wir etwas über das Angular-Framework und für eine bestimmte Version handelt es sich um Version 15. Derzeit steigt die Nachfrage nach Angular von Zeit zu Zeit, unterstützt vom Google-Team und wird immer alle sechs Monate aktualisiert (wenn ich mich nicht irre). In diesem Abschnitt konzentrieren wir uns auf die Verwendung von APIs in Angular, Angular-Diensten, Schnittstellen, Generierung mit Angular-CLI, reaktiven Formularen, Bootstrap 5 und vielen anderen Dingen.
Wenn Sie also immer noch neugierig auf Angular sind, besuchen Sie einfach die Website hier
Beginnen wir mit der Erstellung des Projekts Angular.
Vor der Installation
Ich gehe davon aus, dass Sie NodeJS und NPM installiert haben. In diesem Tutorial verwende ich NodeJS Version 16.18.1 und NPM 8.19.2. Wenn Sie es jedoch noch nicht installiert haben, installieren Sie es zunächst einfach.
Installation
Wir müssen Angular CLI installieren. Führen Sie einfach diesen Befehl aus.
npm i @angular/[email protected]
Danach besteht der nächste Schritt darin, das Projekt zu initiieren. Führen Sie einfach diesen Befehl aus
ng new angular-tutorial-consume-api
Nachdem die Initiierung abgeschlossen ist, versuchen Sie einfach, diesen Befehl auszuführen.
npm run start
In diesem Tutorial möchte ich die API aus dem Link-Tutorial unten verwenden. Wenn Sie also versuchen möchten, zu lernen, wie man eine API mit NestJS und PostgreSQL erstellt, lesen Sie einfach den folgenden Artikel.
So erstellen Sie eine API mit NestJS und PostgreSQLFeature erstellen
Der nächste Schritt besteht darin, eine Modulliste zu erstellen. Mit CLI Angular können wir Module, Dienste, Komponenten, Pipes usw. generieren.
Um viele Dinge zu generieren, können wir sie in einer Zeile wie dieser verwenden.
ng generate module todolist && ng generate service todolist/service/todolist && ng g interface todolist/interface/todolist
ng generate component todolist/todolist
Öffnen Sie app-routing.module.ts und ändern Sie das Routing.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TodolistComponent } from './todolist/todolist/todolist.component';
const routes: Routes = [
{
title: 'Home',
path: '',
component: TodolistComponent
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TodolistModule } from './todolist/todolist.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
TodolistModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
<router-outlet></router-outlet>
Um HttpMethod wie get, post, update und delete zu verwenden, müssen wir HttpClientModule aus @angular/common/http , FormsModule und ReactiveFormsModule importieren, um ein reaktives Formular aus @angular/forms zu verwenden
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TodolistModule } from './todolist/todolist.module';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
ReactiveFormsModule,
FormsModule,
TodolistModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Bevor Sie zum Abschnitt „Dienste“ gehen, füllen Sie einfach die Schnittstelle todolist aus. Öffnen Sie die Datei todolist.interface.ts
export interface Todolist {
id?: number;
title: string;
description: string;
status: string;
due_date: number;
createdAt?: Date;
updatedAt?: Date;
}
In Angular verwenden wir einen Dienst, um eine Liste von Endpunkten und möglicherweise Funktionen zu deklarieren. Öffnen Sie todolist.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable, Type } from '@angular/core';
import { Observable } from 'rxjs';
import { ITodolist } from '../interface/todolist';
@Injectable({
providedIn: 'root'
})
export class TodolistService {
private readonly API_URL = 'http://localhost:3000/todolist';
constructor(
private readonly httpClient: HttpClient
) { }
getAllTodolist(): Observable<ITodolist[]> {
return this.httpClient.get<ITodolist[]>(this.API_URL);
}
getTodolistById(id: number): Observable<ITodolist> {
return this.httpClient.get<ITodolist>(this.API_URL + `?${id}`);
}
createTodolist(body: ITodolist): Observable<ITodolist> {
return this.httpClient.post<ITodolist>(this.API_URL, body);
}
updateTodolist(id: number, body: ITodolist): Observable<ITodolist> {
return this.httpClient.put<ITodolist>(this.API_URL + `/${id}`, body);
}
deleteTodolist(id: number): Observable<Object> {
return this.httpClient.delete<Object>(this.API_URL + `/${id}`);
}
}
- Observable ist der Datentyp zum Streamen der Daten.
Testen des Todolist-Dienstes
Wir müssen sicherstellen, dass der todolist-Dienst kein Fehler ist. Probieren Sie einfach todolist.component.ts aus
Bitte stellen Sie sicher, dass das Dienste-Backend ordnungsgemäß funktioniert
import { AsyncPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { ITodolist } from '../interface/todolist';
import { TodolistService } from '../service/todolist.service';
@Component({
selector: 'app-todolist',
templateUrl: './todolist.component.html',
styleUrls: ['./todolist.component.scss']
})
export class TodolistComponent implements OnInit {
constructor(private readonly todolistService: TodolistService) {}
ngOnInit(): void {
this.todolistService.getAllTodolist().subscribe((response: ITodolist[]) => {
console.log(response, 'res');
})
}
}
Wenn Sie einen Fehler wie im Bild oben erhalten, geraten Sie nicht in Panik. Der Fehler liegt daran, dass das Backend CORS nicht konfiguriert. Die Backend-URL lautet localhost:3000 und die Frontend-URL localhost:4200. Dies ist das Problem, da der Ursprung unterschiedlich ist. Um dieses Problem zu lösen, lassen Sie einfach die CORS im Backend zu.
Nachdem das Backend CORS zulässt, sieht die Antwort wie folgt aus.
Erstellen Sie eine Benutzeroberfläche
In diesem Tutorial möchte ich ngx-bootstrap verwenden, also installieren Sie einfach die Bibliothek, indem Sie diesen Befehl ausführen.
npm i [email protected] bootstrap
npm install @fortawesome/fontawesome-svg-core &&
@fortawesome/free-solid-svg-icons &&
@fortawesome/angular-fontawesome
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TodolistModule } from './todolist/todolist.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ModalModule } from 'ngx-bootstrap/modal';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
TodolistModule,
ModalModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Fügen Sie diese Stile auch in angle.json hinzu.
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
],
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TodolistComponent } from './todolist/todolist.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
@NgModule({
declarations: [
TodolistComponent
],
imports: [
CommonModule,
FontAwesomeModule,
]
})
export class TodolistModule { }
Aktualisieren Sie anschließend die Datei todolist.component.html
<div class="mobile">
<div class="row">
<div class="col-lg-12">
<h3 class="text-center fw-bold mb-3">
My Todolist
</h3>
</div>
</div>
<button type="button" class="btn btn-primary" (click)="createTodo()" style="cursor: pointer">Add Todo</button>
<div class="row my-3">
<div class="col-lg-12">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" aria-label="Search" aria-describedby="search-todolist" #searchInput (keyup)="search(searchInput.value)">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<ul class="list-group">
<ng-container *ngFor="let item of todolist$ | async; let i = index;">
<li class="list-group-item d-flex gap-3 todolist" >
<div class="fw-semibold w-100" (click)="detailTodo(item)">
<span class="pe-1">{{i + 1}}. {{item.title}}</span>
<ng-container [ngSwitch]="item.status">
<span *ngSwitchCase="'progress'" class="badge bg-danger">Progress</span>
<span *ngSwitchCase="'completed'" class="badge bg-primary">Completed</span>
<span *ngSwitchDefault class="badge bg-warning">Todo</span>
</ng-container>
</div>
<div class="d-flex gap-2">
<fa-icon [icon]="faPencil" class="text-success" style="cursor: pointer" (click)="updateTodo(item)"></fa-icon>
<fa-icon [icon]="faTrash" class="text-danger" style="cursor: pointer" (click)="deleteTodo(item)"></fa-icon>
</div>
</li>
</ng-container>
</ul>
</div>
</div>
</div>
- Verwenden Sie den NG-Switch-Case, um den bedingten Rendering-Status zu überprüfen
.mobile {
max-width: 576px !important;
margin: 0 auto;
padding: 16px;
.todolist {
&:hover {
background-color: rgb(206, 206, 206);
animation-name: animateChangeBg;
animation-duration: 1s;
}
}
}
@keyframes animateChangeBg {
from {background-color: rgb(255, 255, 255);}
to {background-color: rgb(206, 206, 206);}
}
- Natürlich haben wir einige Animationen hinzugefügt
import { AsyncPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { delay, filter, map, Observable } from 'rxjs';
import { ITodolist } from '../interface/todolist';
import { TodolistService } from '../service/todolist.service';
import { faTrash, faPencil } from '@fortawesome/free-solid-svg-icons';
import { DeleteTodolistComponent } from './delete-todolist/delete-todolist.component';
import { FormTodolistComponent } from './form-todolist/form-todolist.component';
import { DetailTodolistComponent } from './detail-todolist/detail-todolist.component';
@Component({
selector: 'app-todolist',
templateUrl: './todolist.component.html',
styleUrls: ['./todolist.component.scss']
})
export class TodolistComponent implements OnInit {
todolist$?: Observable<ITodolist[]>;
searchTodolist = new FormControl();
modalRef?: BsModalRef;
faTrash = faTrash;
faPencil = faPencil;
constructor(private readonly todolistService: TodolistService, private modalService: BsModalService) {}
ngOnInit(): void {
this.getTodolist();
}
getTodolist() {
this.todolist$ = this.todolistService.getAllTodolist();
}
search(searchTodolist: string): void {
this.todolist$ = this.todolistService.getAllTodolist().pipe(
delay(500),
map((todolist: ITodolist[]) => {
const filterData = todolist.filter((item) => {
const checkKeyword = new RegExp(`${searchTodolist}`, 'ig')
if (!item.title.match(checkKeyword)) {
return;
}
return {...item}
})
return filterData;
}),
)
}
createTodo(): void {
this.modalRef = this.modalService.show(FormTodolistComponent, {
initialState: {
data: undefined
},
animated: true,
});
this.modalRef.content.onClose.subscribe((response: boolean) => {
if (response) {
this.getTodolist();
}
})
console.log('create');
}
updateTodo($event: ITodolist): void {
this.modalRef = this.modalService.show(FormTodolistComponent, {
initialState: {
data: $event
},
animated: true,
});
this.modalRef.content.onClose.subscribe((response: boolean) => {
if (response) {
this.getTodolist();
}
})
console.log('update');
}
deleteTodo($event: ITodolist): void {
this.modalRef = this.modalService.show(DeleteTodolistComponent, {
initialState: {
data: $event
},
animated: true,
});
this.modalRef.content.onClose.subscribe((response: boolean) => {
if (response) {
this.getTodolist();
}
})
console.log('delete');
}
detailTodo($event: ITodolist): void {
this.modalRef = this.modalService.show(DetailTodolistComponent, {
initialState: {
data: $event
},
animated: true,
});
}
}
- Wir deklarieren die Variable todolist$ mit dem Typ Observable, um die Funktion getAll vom Dienst todolist zuzuweisen.
- Die Funktionssuche dient zum Suchen oder Filtern von Daten, die nach Benutzereingaben zu erledigen sind. Dies ist der Vorteil der Verwendung von rxjs: Wir können mehrere Operatoren verwenden, um Daten zu manipulieren.
- Wir müssen Inhalte bei Close abonnieren, um das Ereignis abzuhören. Wenn dies der Fall ist, wird die API automatisch aufgerufen, um alle Todolisten abzurufen.
Wir müssen jedoch eine Aktion wie Erstellen, Detaillieren, Aktualisieren und Löschen hinzufügen.
Für die modale Aktualisierung, Detailerstellung und Löschung generieren wir eine Komponente und trennen sie von der übergeordneten Komponente.
ng generate component todolist/todolist/form-todolist
ng generate component todolist/todolist/delete-todolist
ng generate component todolist/todolist/detail-todolist
npm i ngx-toastr
npm i @angular/animations --save
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TodolistModule } from './todolist/todolist.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ModalModule } from 'ngx-bootstrap/modal';
import { ToastrModule } from 'ngx-toastr';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
TodolistModule,
ModalModule.forRoot(),
ToastrModule.forRoot({
timeOut: 2000,
positionClass: 'toast-bottom-right',
preventDuplicates: true,
}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Fügen Sie diese Stile auch in angle.json hinzu.
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/ngx-toastr/toastr.css"
],
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { ToastrService } from 'ngx-toastr';
import { Observable, Subject } from 'rxjs';
import { ITodolist } from '../../interface/todolist';
import { TodolistService } from '../../service/todolist.service';
@Component({
selector: 'app-delete-todolist',
templateUrl: './delete-todolist.component.html',
styleUrls: ['./delete-todolist.component.scss']
})
export class DeleteTodolistComponent {
public data!: ITodolist;
public onClose: Subject<boolean> = new Subject();
constructor(
private bsModalService: BsModalService,
private todolistService: TodolistService,
private toastrService: ToastrService
) {
}
closeModal(): void {
this.bsModalService.hide();
}
deleteData(): void {
let id = -1;
if (this.data) {
id = this.data.id
}
this.todolistService.deleteTodolist(id).subscribe(response => {
this.toastrService.success('Success', 'Success Delete!');
this.onClose.next(true);
this.closeModal();
}, (err: HttpErrorResponse) => {
this.toastrService.success('Failed', 'Failed Delete!');
throw new Error(err.error);
});
}
}
Öffnen Sie dann die Datei delete-modal.component.html
<div class="modal-header">
<h4 class="modal-title pull-left">Delete Todo</h4>
<button type="button" class="btn-close close pull-right" aria-label="Close" (click)="closeModal()">
<span aria-hidden="true" class="visually-hidden" (click)="closeModal()">×</span>
</button>
</div>
<div class="modal-body">
Are you sure want to delete this todo with the title {{data.title}}?
</div>
<div class="modal-footer">
<button type="button" (click)="closeModal()" class="btn btn-secondary w-25">
Back
</button>
<button type="button" (click)="deleteData()" class="btn btn-danger w-25">Yes</button>
</div>
Als nächstes aktualisieren Sie die Komponente detail-toodlist.component.html
<div class="modal-header">
<h4 class="modal-title pull-left">Detail Todo</h4>
<button type="button" class="btn-close close pull-right" aria-label="Close" (click)="closeModal()">
<span aria-hidden="true" class="visually-hidden" (click)="closeModal()">×</span>
</button>
</div>
<div class="modal-body">
<table class="table table-borderless">
<tr>
<td>Title</td>
<td>:</td>
<td>{{data.title}}</td>
</tr>
<tr>
<td>Description</td>
<td>:</td>
<td>{{data.description}}</td>
</tr>
<tr>
<td>Status</td>
<td>:</td>
<td>
<ng-container [ngSwitch]="data.status">
<span *ngSwitchCase="'progress'" class="badge bg-danger">Progress</span>
<span *ngSwitchCase="'completed'" class="badge bg-primary">Completed</span>
<span *ngSwitchDefault class="badge bg-warning">Todo</span>
</ng-container>
</td>
</tr>
<tr>
<td>Due Date</td>
<td>:</td>
<td>{{dueDate}}</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" (click)="closeModal()" class="btn btn-secondary w-25">
Back
</button>
</div>
import { Component, OnInit } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { ITodolist } from '../../interface/todolist';
@Component({
selector: 'app-detail-todolist',
templateUrl: './detail-todolist.component.html',
styleUrls: ['./detail-todolist.component.scss']
})
export class DetailTodolistComponent implements OnInit{
public data!: ITodolist;
public dueDate!: string;
constructor(private bsModalService: BsModalService) {
}
ngOnInit(): void {
this.dueDate = new Date(Number(this.data.due_date)).toLocaleDateString();
}
closeModal(): void {
this.bsModalService.hide();
}
}
Die Vorschau-Detailliste sieht so aus
Als nächstes in der Komponente form-todolist. Diese Komponente wird zum Erstellen und Aktualisieren von Todolisten verwendet.
Öffnen Sie die Datei form-todolist.component.html
<div class="modal-header">
<h4 class="modal-title pull-left">{{title}}</h4>
<button type="button" class="btn-close close pull-right" aria-label="Close" (click)="closeModal()">
<span aria-hidden="true" class="visually-hidden">×</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="formTodolist">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" placeholder="Learning something new" formControlName="title">
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" rows="3" formControlName="description"></textarea>
</div>
<div class="mb-3">
<select class="form-select" aria-label="Default select example" formControlName="status">
<option selected>Open this select menu</option>
<option value="todo">Todo</option>
<option value="progress">Progress</option>
<option value="completed">Completed</option>
</select>
</div>
<div class="mb-3">
<label for="due_date" class="form-label">Due Date</label>
<input type="date" class="form-control" id="due_date" formControlName="due_date">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" (click)="closeModal()" class="btn btn-secondary w-25">
Back
</button>
<button type="button" (click)="submit()" class="btn btn-danger w-25" [disabled]="formTodolist.invalid">Submit</button>
</div>
Öffnen Sie die Datei form-todolist.component.ts
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { ToastrService } from 'ngx-toastr';
import { Subject } from 'rxjs';
import { ITodolist } from '../../interface/todolist';
import { TodolistService } from '../../service/todolist.service';
@Component({
selector: 'app-form-todolist',
templateUrl: './form-todolist.component.html',
styleUrls: ['./form-todolist.component.scss']
})
export class FormTodolistComponent implements OnInit {
public data?: ITodolist;
public title!: string;
public onClose: Subject<boolean> = new Subject();
formTodolist: FormGroup = new FormGroup({
title: new FormControl('', [Validators.required]),
description: new FormControl('', [Validators.required]),
status: new FormControl('todo', [Validators.required]),
due_date: new FormControl('', [Validators.required])
})
constructor(
private bsModalService: BsModalService,
private todolistService: TodolistService,
private toastrService: ToastrService
) {
}
ngOnInit(): void {
if (this.data) {
this.title = 'Update Todo'
this.doInitFormUpdate();
} else {
this.title = 'Create Todo'
}
}
doInitFormUpdate() {
this.formTodolist.setValue({
title: this.data?.title,
description: this.data?.description,
status: this.data?.status,
due_date: this.setDefaultDate(Number(this.data!.due_date))
})
}
setDefaultDate(due_date: number) {
const date = new Date(due_date)
const year = date.getFullYear()
let month: number | string = date.getMonth() + 1
let day: number | string = date.getDate()
if (month < 10) month = '0' + month
if (day < 10) day = '0' + day
const finalDate = `${year}-${month}-${day}`
return finalDate;
}
doCreate(body: ITodolist) {
body.due_date = Number(new Date(this.formTodolist.value.due_date).getTime());
this.todolistService.createTodolist(body).subscribe(response => {
this.toastrService.success('Success', 'Success Create!');
this.onClose.next(true);
this.closeModal();
}, (err: HttpErrorResponse) => {
this.toastrService.error('Failed', 'Failed Create!');
throw new Error(err.error);
})
}
doUpdate(id: number, body: ITodolist) {
body.due_date = Number(new Date(this.formTodolist.value.due_date).getTime());
this.todolistService.updateTodolist(id, body).subscribe(response => {
this.toastrService.success('Success', 'Success Update!');
this.onClose.next(true);
this.closeModal();
}, (err: HttpErrorResponse) => {
this.toastrService.error('Failed', 'Failed Update!');
throw new Error(err.error);
})
}
submit() {
if (this.data) {
this.doUpdate(this.data!.id, this.formTodolist.value);
} else {
this.doCreate(this.formTodolist.value);
}
}
closeModal(): void {
this.bsModalService.hide();
}
}
- Wenn die Variablendaten undefiniert sind, bedeutet dies den Erstellungsmodus, andernfalls den Aktualisierungsmodus
- Die Funktion setDefaultDate dient dazu, einen Standardwert für das Elementdatum festzulegen.
- doCreate und doUpdate sind Funktionen, um den Endpunkt per Modusform zu erreichen
- Die Funktion doInitFormUpdate dient dazu, einen Wert für den Aktualisierungsmodus festzulegen.
- Die Funktion „submit()“ ist eine Funktion zum Auslösen der Erstellung oder Aktualisierung von Daten.
Dann ist dies die Vorschau-Update-Liste
Endlich haben wir das Tutorial abgeschlossen, es ist etwas zu lang, aber es ist in Ordnung, weil wir in diesem Tutorial viel lernen. Jedenfalls habe ich für diesen Code einen Push auf mein GitHub-Repository durchgeführt
Verweise
#Angular #Angular15 #TutorialAngular #ReactiveForm #ConsumeRestApi #RESTAPI

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































