Waivio

Recommended Posts

Creation Of The Hammurabi Game Part 7 - Rebuild The Frontend In Angular

5 comments

achimmertens1 K2 years agoPeakD12 min read

Hello Hive-, Java-, node.js-, React-, Angular-, docker-freaks,
Hello all others,

In the long run I want to create a simple game with the name "Hammurabi", which uses Hive content to influence the run of play.
https://images.hive.blog/0x0/https://files.peakd.com/file/peakd-hive/achimmertens/23vhpzAunKDXtohLemRj1DKQegshSGYBNPfMAtKrE3VyW8MDJXQk7wxpvUasbdGV6aqb9.png

In my last documentation I showed, how I have added some buttons in React, which enabled my frontend to connect to my Java code to Create, Read, Update or Delete (CRUD) some data in my ElasticSearch database.
https://images.hive.blog/0x0/https://files.peakd.com/file/peakd-hive/achimmertens/23w3CwUTSSHTvRg4R6AuStqgN9ZsiNYiNuB5qNgVnAebjLvJDrYuSCrH3pvThkvu5FDcP.png

Now I want to rebuild this React code into Angular code. (This is because I want to develop in Angular in future, because I need it in my job and most people in my environment say Angular is better then React.)


Content:


Install Angular

After merging my code into a safe branch, I deleted my frontend folder.
Then I typed into the console:

npm install -g @angular/cli

https://files.peakd.com/file/peakd-hive/achimmertens/23tGVuuBVBGEM7uhwZWNsLRe922G9rP4zYJHtsGmduCsGSxHRAje6LnVoHLVeGVwXjPtF.png

cd ..
ng new frontend

I used default settings:

https://files.peakd.com/file/peakd-hive/achimmertens/23tbHLQXFFzTqsvcis8gs8kNcJGL35vC2LtDDJ1yQLhhbvQE7Wu45L8hj8dTm5nTphUz1.png

https://files.peakd.com/file/peakd-hive/achimmertens/23tRtN6vEmuykjeteVJPRGBANCSgiJnxXE8mTAXxrn5RD82J2EETi2yA8gDmyzGjczmjT.png

Now I can open the code with my favorite IDE, which is Visual Studio Code:
When opening the folder for the first time, I was asked to install the "Angular Language Service" package. I installed the plugin and also "Angular Schematics":

https://files.peakd.com/file/peakd-hive/achimmertens/23tGc1BNXbRQT3K5EjY4onJCvysoC8TEBxpnEHJqgi1E8dewbQxhWY2jNL9Feh7y3tp5Q.png

https://files.peakd.com/file/peakd-hive/achimmertens/23t77MJS6EyBLhu5Z5Zgaq7D7K6VFqvE886LiAMjGxTR8wJKm8JdLB9ZcZyQwjpDEPYaE.png

Now I am able to start the default Installation either via console with "ng serve --open" or via VisualStudioCode, by activating npm-scripts in the timeline and then clicking on "NPM-SKRIPTS/start ng serve":

https://files.peakd.com/file/peakd-hive/achimmertens/23t78yK4NjtjcDLwa7gvscVQQnvqrBqrCZpUjuFySWRSvTJeByFQWvzZHcrqs1LFCPrkY.png
 
https://files.peakd.com/file/peakd-hive/achimmertens/Eo1wMTqEEDaeMKAh9pWDDAbd8UGKfTMzfHUsbKEAGZMhDp7Rhb52rzCvdMdvLCxv8no.png

https://files.peakd.com/file/peakd-hive/achimmertens/23t773RVEfEECZqSrrf1uco4JEQfniksH4J2mUu44pTGuKu4xrFE6ArhjmGRgcTwv1MQX.png

You can see the result on your browser with https://localhost:4200 (or another port, if there are several angular instances running simultanesly):

https://files.peakd.com/file/peakd-hive/achimmertens/23w387w1ZB84gZraHCqAgwhfznNMvHMqULeatUZsPmyezqaSD9mtA2X4JrhmqkjWPXHN5.png

Yes, and that is a good advise for beginners: read the tutorial and work through the Tour of Heros. How to setup the environment is also discribed there.


Delete Welcome Page And Start From Scratch

We need to change some files:
app.component.ts: There we exchange the title into "Frontend":

https://files.peakd.com/file/peakd-hive/achimmertens/23t76pMtPZJ1uFfeHxpkbvdWwzQPgdXdpSnUtjpp7a8qw9FHfM3yxdgTG4jijQAziSjWJ.png
App.component.html: We delete everything and exchange it with:
<h2>{{title}}</h2>

https://files.peakd.com/file/peakd-hive/achimmertens/EpLfvK6whNMm5C8vr9AzXskpm3wFts3RbhUCF1PKnzWv657mSPSDvWQFRgjfVP74FXB.png

Creation Of The First Components

Create The Accounts Component

I want to rebuild and adapt a little the tour of Heros. My "Heroes" are "Accounts". If you want to follow my steps, go to a command shell, go to the frontend folder and type in:

ng generate component accounts

(If it doesn't work, because you use Windows Powershell, try this before:

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Taken from: https://angular.io/guide/setup-local

)
 
https://files.peakd.com/file/peakd-hive/achimmertens/23tRvMk44dV6y61duzTFy7kHbAt3zi3Gq4KEcxiFaSVRMjJwfp8VPK7Ckz1FtVYuqndkT.png

Some new files have been created:

https://files.peakd.com/file/peakd-hive/achimmertens/Eo43mfN4aig2rcjQ2AnF4XASzLNPEXo9VhjUNS2Ko9QhgsB1L5uzE14ACWqTELW3ftG.png

Now we create the interface app/account.ts and fill in the format of our Account data:

https://files.peakd.com/file/peakd-hive/achimmertens/23t76kXYWd2XRoUkP2D7N2kYocoZqR4PYpJXaMRNAXRgoZvCPGwm4DyF5PXfRhFMyEgvn.png
This is an interface, which is used in other components.

We need some more components.

To describe it all in detail doesn't make sense, because at the end it is more or less an adaption of the Tour of Heros tutorial.
You can see my complete code on github and the most important files below.

Here are some of the steps I did to create more components:
 

ng generate component account-detail

https://files.peakd.com/file/peakd-hive/achimmertens/23tbMP43MtapHtQJhQU183TZMENPeTMTtKJnYkRPQ8FZoqvVSnS3ERxVQpn72wQqjhyke.png

ng generate service account

https://files.peakd.com/file/peakd-hive/achimmertens/23tRxigTW36Wouup1wAMVyVWbsXwRXcpUnFLyisA5qznZoCW852hRG99jKCJvgEFxaaBv.png

ng generate module app-routing --flat --module=app

(see also: https://angular.io/tutorial/toh-pt5)

ng generate component dashboard

The part with the HTTPClientModule is not in the tutorial, so I will explaint it here in detail:

CRUD with HttpClientModule

In the Tour of Heros tutorial they work with a mock. So their data is listed in the code and the API connection is intercepted via the HttpClientInMemoryWebApiModule.
Here in my case I have a API backend server, which runs on my Raspberry Pi on https://192.168.2.121:8080

So I needed to update their hero.service.ts ( in my case account.service.ts) to get the data from this real API. Here is the top of this service code:

https://files.peakd.com/file/peakd-hive/achimmertens/Eo1wMU9xjeH4jw7zmH4pdXTHbkAsDtPSadck7atjLZtA8UpdotH6DwzgoBW5PVaR63z.png

GET list of accounts

With the following method from account.service.ts we get a JSON string from the API:

https://files.peakd.com/file/peakd-hive/achimmertens/Eo44Mxv7VfpxxJE3mEE5teReynt1qvAdgNwULqx5jvVTG7C3WmR2YgzEuENvqYBWk2N.png

The result in the browser is (after adapting also some other code components):

https://files.peakd.com/file/peakd-hive/achimmertens/23tmmXSdULvnhoSrzSRYZ1Z4qC18pFrqDYgkA7XVSe1g2J2G3pzZgVwfx2LKcvENtcm8P.png

In the beginning, I got a lot of "CORS Missing Allow Origin" messages:

https://files.peakd.com/file/peakd-hive/achimmertens/23u6Z47xHxWHbPRoFDS4pzhfqMr9PYy5Lgu8JAMjELRUNbPrcXTmzQ2osKDJtd2p9jZUQ.png

This was, because I just put it the wrong account-url. (It can also happen, if the http-headers are not sufficient).
After entering the rigt URL, the error was gone.

Now, to show a single element of this JSON string, we can do it in the html component by cascading the JSON array with {{accounts[x].content[y].id (or .name or .nickname or .logindate):

https://files.peakd.com/file/peakd-hive/achimmertens/23tGVhrLx8Xga8JyGRiW2kJ5QT49JdLNAmArAUXPjHGFGdrfyDBshJcberLY8fLVMqzFv.png

(But this works only, if I define in the account.service the content as "any" (see above). If I would define it as 'var content:Account[]=[];' as it is done in the tutorial and is needed for searching, I was not able find objects in the JSON array any more. (Maybe I find a solution for that later.))

In this html-file we create a loop, that runs through all acounts (here: accounts[0].content), and shows their elements account.id, account.name, account.nickname and account.logindate:
 
https://files.peakd.com/file/peakd-hive/achimmertens/Eoiba5xQk2Greqs8uqpAofu62EvusJkaipJxTQuig91GGkxNS7TKk3AbSeTVPshozDE.png

GET single account

The following method from account.service.ts shows, how we get a single account with a given ID:

https://files.peakd.com/file/peakd-hive/achimmertens/Eo44P3C3yze4GJa7b9AYrKX9XNwZLfTFe2E8ZitNy76wTYxoFYswjstGiNwuUYXHYaF.png

In the account-detail.component.ts I execute this accountService.getAccount method with the wished account-ID:

https://files.peakd.com/file/peakd-hive/achimmertens/Eo1vw7hkVM4LbNE6iWiUgzrP8o9CJ3NwwS8VWb2z8ChFihLUhwfFAzWJz8oGW62H8gb.png

Now I can open the account-detail page with the wished ID: "/detail/{{account.id}}":

https://files.peakd.com/file/peakd-hive/achimmertens/23tGViC1JLa8dSaFASETyU58snf6P4Gpe1YsFDDYKQMiyckuKUnzGR3sGbfW5eVKkdHvK.png

The account-detail.html is opened with the given ID. Because of the getAccount method in account-detail.component.ts, the complete dataset of "account" is known.

https://files.peakd.com/file/peakd-hive/achimmertens/23swhwXnaQX6VHTGSveRrYyxyxRiZQgm5eU784HPJucsqzbb5deBEW1pum484FYNEQ9P3.png

The result is like this:

https://files.peakd.com/file/peakd-hive/achimmertens/23tHbKgySHhNx1VpGSpYVVnmYSzFZNKT67mVRC7KKo3sQ7bAQErSJbD8QK42tBSUp68no.png

Delete An Account

I added the following method to account.service.ts:

https://files.peakd.com/file/peakd-hive/achimmertens/23t76buhD36HTuAFALGtVWPSpXtnB1aF4EZHV7u6Y2Lre4DYgZqt6CMQPmWbMtst7uvGj.png

And this to accounts.component.ts:

https://files.peakd.com/file/peakd-hive/achimmertens/48GCANqVtsxzfoCkkVZtZtDui9CaqqTvrVJUGVXwGxFJCzXjZqm1ZhpVMrP9rSvm4C.png

Now, I am able to delete an account in accounts.component.html. This generates a button to delete the marked account:

https://files.peakd.com/file/peakd-hive/achimmertens/23tbGsSQkViwUgMSTrsE5iMBHirUhJcMW2ggWjmKW2Lp1nELuc2WDMSN6aLijZtZ8wRrW.png

https://files.peakd.com/file/peakd-hive/achimmertens/23wgma9c78RhFsrPM2et57KfjqgHgDwFsdqfurLh73i8RyGHtJfrhwTCPT68YgEoNkFth.png

Update And Add An Account

For this showcase I was lazy and mixed both together. Because a POST request can also update or generate an account in my database.

I added a router component to "add" a user with the ID "0" in the frontpage:

https://files.peakd.com/file/peakd-hive/achimmertens/23tGViCfQWp9kRW9mWKeuZBfM11hiVR4VnJ94CrTwtptLMRvKAAT2PTKtVrohM8n936Sb.png

The app-routing.modules.ts didn't change and looks like this:

https://files.peakd.com/file/peakd-hive/achimmertens/23tGVqXUDwUu7yKLQkHYnEQPcfKTCaqHekRWFYe4fspow4rnePSadW3v2w5PTgBsGrkLp.png

The Detail pages shows now the dummy account with ID=0.
To create a new account, one should change this ID and fill in the rest of the fields. (As I said, this is realy lazy developing ;-))

https://files.peakd.com/file/peakd-hive/achimmertens/23tSym8QEvXUthreAnrS7vfK4ZVprECA9xarpvXUF3BVKNBxbCoda96h3rSj8QfVWRcdN.png

By clicking the "save" button, in account-details.ts the method "save" is executed:

https://files.peakd.com/file/peakd-hive/achimmertens/23t77B9E8yEUv2zu9y4ZcX8ZvGq8PiLVkwZR2djzP9XBgVoA5FAwqGHBqwnXzeNkfhEBE.png

And this uses the accountService.addAccount method to post the data to the REST API server:

https://files.peakd.com/file/peakd-hive/achimmertens/23swiJip2CjpN7WiFdDNFWHwYoaYhzKsMwD7wZ6RF7ZNUcpMA3CA3Mkh2bjjmFRy1gaVd.png

The most important files:

As Is said, you can see the complete code on github
The css files are not changed and can be taken from Heros tutorial.

Here are the files, (most of) that I have adapted:

https://files.peakd.com/file/peakd-hive/achimmertens/23tGXhXRSJ8TRm3MPsgnc9itSri3dMgtHSQ2DeadAYfukR85BRs3d6hCSYLhtge3yuWea.png
 

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { AccountsComponent } from './accounts/accounts.component';
import { AccountDetailComponent } from './account-detail/account-detail.component';
import { MessagesComponent } from './messages/messages.component';
import { AppRoutingModule } from './app-routing.module';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
&nbsp;&nbsp;declarations: [
&nbsp;&nbsp;&nbsp;&nbsp;AppComponent,
&nbsp;&nbsp;&nbsp;&nbsp;AccountsComponent,
&nbsp;&nbsp;&nbsp;&nbsp;AccountDetailComponent,
&nbsp;&nbsp;&nbsp;&nbsp;MessagesComponent,
&nbsp;&nbsp;&nbsp;&nbsp;DashboardComponent
&nbsp;&nbsp;],
&nbsp;&nbsp;imports: [
&nbsp;&nbsp;&nbsp;&nbsp;BrowserModule,
&nbsp;&nbsp;&nbsp;&nbsp;FormsModule,
&nbsp;&nbsp;&nbsp;&nbsp;AppRoutingModule,
&nbsp;&nbsp;&nbsp;&nbsp;HttpClientModule
&nbsp;&nbsp;],
&nbsp;&nbsp;providers: [],
&nbsp;&nbsp;bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';
@Component({
&nbsp;&nbsp;selector: 'app-root',
&nbsp;&nbsp;templateUrl: './app.component.html',
&nbsp;&nbsp;styleUrls: ['./app.component.css']
})
export class AppComponent {
&nbsp;&nbsp;title = 'Frontend';
}

app.component.html

<h2>{{title}}</h2>
<router-outlet>
&nbsp;&nbsp;<nav>
&nbsp;&nbsp;&nbsp;&nbsp;<a routerLink="/dashboard">Dashboard</a>
&nbsp;&nbsp;&nbsp;&nbsp;<a routerLink="/accounts">Accounts</a>
&nbsp;&nbsp;&nbsp;&nbsp;<a routerLink="/detail/0"> Add Account</a>
&nbsp;&nbsp;</nav>
</router-outlet>
<hr />
<app-messages></app-messages>

app-routing.modules.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AccountDetailComponent } from './account-detail/account-detail.component';
import { AccountsComponent } from './accounts/accounts.component';
const routes: Routes = [
&nbsp;&nbsp;{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
&nbsp;&nbsp;{ path: 'dashboard', component: DashboardComponent },
&nbsp;&nbsp;{ path: 'detail/:id', component: AccountDetailComponent },
&nbsp;&nbsp;{ path: 'accounts', component: AccountsComponent }
];
@NgModule({
&nbsp;&nbsp;imports: [RouterModule.forRoot(routes)],
&nbsp;&nbsp;exports: [RouterModule]
})
export class AppRoutingModule { }

 

message.service.ts

import { Injectable } from '@angular/core';
@Injectable({
&nbsp;&nbsp;providedIn: 'root',
})
export class MessageService {
&nbsp;&nbsp;messages: string[] = [];
&nbsp;&nbsp;add(message: string) {
&nbsp;&nbsp;&nbsp;&nbsp;this.messages.push(message);
&nbsp;&nbsp;}
&nbsp;&nbsp;clear() {
&nbsp;&nbsp;&nbsp;&nbsp;this.messages = [];
&nbsp;&nbsp;}
}

accounts.ts

export interface Account {
&nbsp;&nbsp;&nbsp;&nbsp;id: number;
&nbsp;&nbsp;&nbsp;&nbsp;name: string;
&nbsp;&nbsp;&nbsp;&nbsp;nickname: string;
&nbsp;&nbsp;&nbsp;&nbsp;logindate: Date;
}

account-detail.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Account } from '../account';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { AccountService } from '../account.service';
@Component({
&nbsp;&nbsp;selector: 'app-account-detail',
&nbsp;&nbsp;templateUrl: './account-detail.component.html',
&nbsp;&nbsp;styleUrls: ['./account-detail.component.css']
})
export class AccountDetailComponent implements OnInit {
&nbsp;// @Input() account?: Account;
&nbsp;&nbsp;account: Account | undefined;
&nbsp;&nbsp;
&nbsp;&nbsp;constructor( 
&nbsp;&nbsp;&nbsp;&nbsp;private route: ActivatedRoute,   
&nbsp;&nbsp;&nbsp;&nbsp;private location: Location, 
&nbsp;&nbsp;&nbsp;&nbsp;private accountService:AccountService) { 
&nbsp;&nbsp;}
&nbsp;&nbsp;ngOnInit(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.getAccount();
&nbsp;&nbsp;}
&nbsp;&nbsp;goBack(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.location.back();
&nbsp;&nbsp;}
&nbsp;&nbsp;getAccount(): void {
&nbsp;&nbsp;&nbsp;&nbsp;const id = Number(this.route.snapshot.paramMap.get('id'));
&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.getAccount(id)
&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(account => this.account = account);
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;save(): void {
&nbsp;&nbsp;&nbsp;&nbsp;if (this.account) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.addAccount(this.account)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(() => this.goBack());
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
}

account-detail.component.html

<div *ngIf="account">
&nbsp;&nbsp;&nbsp;&nbsp;<h2>{{account.name | uppercase}} Details</h2>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="account-id">Account ID: </label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="account-id" [(ngModel)]="account.id" placeholder="id">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="account-name">Account name: </label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="account-name" [(ngModel)]="account.name" placeholder="name">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="account-nickname"> Nickname: </label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="account-nickname" [(ngModel)]="account.nickname" placeholder="nickname">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<label for="account-logindate"> Logindate: </label>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input id="account-logindate" [(ngModel)]="account.logindate" placeholder="logindate">
&nbsp;&nbsp;&nbsp;&nbsp;</div>
&nbsp;&nbsp;&nbsp;&nbsp;<button type="button" (click)="goBack()">go back</button>
&nbsp;&nbsp;&nbsp;&nbsp;<button type="button" (click)="save()">save</button>
&nbsp;&nbsp;</div>

accounts.component.ts

import { Component, OnInit } from '@angular/core';
import { Account } from '../account';
import { Level1 } from '../level1';
import { AccountService } from '../account.service';
import { MessageService } from '../message.service';
//import { Observable } from 'rxjs';
@Component({
&nbsp;&nbsp;selector: 'app-accounts',
&nbsp;&nbsp;templateUrl: './accounts.component.html',
&nbsp;&nbsp;styleUrls: ['./accounts.component.css']
})
export class AccountsComponent implements OnInit {
&nbsp;&nbsp;accounts: any; // accounts: Account[]=[] doesn't work, because then "content[]" is not found in html
&nbsp;&nbsp;content: Level1[] = [];
&nbsp;&nbsp;selectedAccount?: Account;
&nbsp;&nbsp;constructor(private accountService: AccountService, private messageService: MessageService) { }
&nbsp;&nbsp;ngOnInit(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.getAccounts();
&nbsp;&nbsp;&nbsp;&nbsp;//this.getContent();
&nbsp;&nbsp;}
&nbsp;&nbsp;onSelect(account: Account): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.selectedAccount = account;
&nbsp;&nbsp;&nbsp;&nbsp;this.messageService.add(`AccountsComponent: Selected hero id=${account.id}`);
&nbsp;&nbsp;}
&nbsp;&nbsp;getAccounts(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.getAccounts()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(accounts => this.accounts = accounts);
&nbsp;&nbsp;}
&nbsp;&nbsp;/* 
&nbsp;&nbsp;getContent(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.getLevel1()
&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(content => this.content= content);
&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;*/
&nbsp;&nbsp;delete(account: Account): void {
&nbsp;&nbsp;&nbsp;&nbsp;//this.accounts = this.accounts.filter(h => h !== account);  // Doesn't work with accounts:any, we need accounts: Account[]=[]
&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.deleteAccount(account.id).subscribe();
&nbsp;&nbsp;}
}

accounts.component.html

<h2>My Accounts</h2>
Die Account-ID vom ersten Datensatz lautet: {{accounts[0].content[0].id}}
<ul class="accounts">
&nbsp;&nbsp;<li *ngFor="let account of accounts[0].content">
&nbsp;&nbsp;&nbsp;&nbsp;<a routerLink="/detail/{{account.id}}">    
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="badge">ID: {{account.id}}</span> Name: {{account.name}}, NickName: {{account.nickname}}, Logindate: {{account.logindate}}
&nbsp;&nbsp;&nbsp;&nbsp;</a>
&nbsp;&nbsp;&nbsp;&nbsp;<button type="button" class="delete" title="delete account"
&nbsp;&nbsp;&nbsp;&nbsp;(click)="delete(account)">x</button>
&nbsp;&nbsp;</li>

dashboard.components.ts

import { Component, OnInit } from '@angular/core';
import { AccountService } from '../account.service';
@Component({
&nbsp;&nbsp;selector: 'app-dashboard',
&nbsp;&nbsp;templateUrl: './dashboard.component.html',
&nbsp;&nbsp;styleUrls: [ './dashboard.component.css' ]
})
export class DashboardComponent implements OnInit {
&nbsp;&nbsp;accounts: any;
&nbsp;&nbsp;constructor(private accountService: AccountService) { }
&nbsp;&nbsp;ngOnInit(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.getAccounts();
&nbsp;&nbsp;}
&nbsp;&nbsp;getAccounts(): void {
&nbsp;&nbsp;&nbsp;&nbsp;this.accountService.getAccounts()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(accounts => this.accounts = accounts);
&nbsp;&nbsp;}
}

dashboard.component.html

<h2>Menu</h2>
<h3> Rules:</h3>
Lorem Ipsum ...
<ul class="accounts-menu">
&nbsp;&nbsp;<a *ngFor="let number of [].constructor(5), let x = index">
&nbsp;&nbsp;&nbsp;&nbsp;{{accounts[0].content[x].name}}
&nbsp;&nbsp;</a>
</ul>
<div *ngFor="let number of accounts[0].content, let x = index">
&nbsp;&nbsp;id: {{accounts[0].content[x].id}}
&nbsp;&nbsp;name: {{accounts[0].content[x].name}}
&nbsp;&nbsp;nickname: {{accounts[0].content[x].nickname}}
&nbsp;&nbsp;logindate: {{accounts[0].content[x].logindate}}
</div>

message.component.ts

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';
@Component({
&nbsp;&nbsp;selector: 'app-messages',
&nbsp;&nbsp;templateUrl: './messages.component.html',
&nbsp;&nbsp;styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
&nbsp;&nbsp;constructor(public messageService: MessageService) {}
&nbsp;&nbsp;ngOnInit() {
&nbsp;&nbsp;}
}

message.component.html

<div *ngIf="messageService.messages.length">
&nbsp;&nbsp;&nbsp;&nbsp;<h2>Messages</h2>
&nbsp;&nbsp;&nbsp;&nbsp;<button type="button"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="clear"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(click)="messageService.clear()">Clear messages</button>
&nbsp;&nbsp;&nbsp;&nbsp;<div *ngFor='let message of messageService.messages'> {{message}} </div>
&nbsp;&nbsp;</div>

account.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { Account } from './account';
import { MessageService } from './message.service';
import { Level1 } from './level1';

@Injectable({
&nbsp;&nbsp;providedIn: 'root'
})
export class AccountService {
&nbsp;&nbsp;private accountsUrl = "https://192.168.2.121:8080/api/accounts"
&nbsp;&nbsp;private accountUrl = "https://192.168.2.121:8080/api/account"
&nbsp;&nbsp;private deleteUrl = "https://192.168.2.121:8080/api/delete"
&nbsp;&nbsp;httpOptions = {
&nbsp;&nbsp;&nbsp;&nbsp;headers: new HttpHeaders({
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'Content-Type': 'application/json',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'Accept': '*/*'
&nbsp;&nbsp;&nbsp;&nbsp;})
&nbsp;&nbsp;};

&nbsp;&nbsp;constructor(
&nbsp;&nbsp;&nbsp;&nbsp;private http: HttpClient,
&nbsp;&nbsp;&nbsp;&nbsp;private messageService: MessageService,
&nbsp;&nbsp;) { }

&nbsp;&nbsp;/** GET accounts from the server */
&nbsp;&nbsp;getAccounts(): Observable<Account[]> {
&nbsp;&nbsp;&nbsp;&nbsp;//var content:Account[]=[];
&nbsp;&nbsp;&nbsp;&nbsp;var content: any;
&nbsp;&nbsp;&nbsp;&nbsp;content = this.http.get<Account[]>(this.accountsUrl)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.pipe(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tap(_ => this.log('fetched accounts')),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catchError(this.handleError<Account[]>('getAccounts', []))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);
&nbsp;&nbsp;&nbsp;&nbsp;this.log("Der Inhalt von content ist:" + JSON.stringify(content));
&nbsp;&nbsp;&nbsp;&nbsp;return content;
&nbsp;&nbsp;}

&nbsp;&nbsp;/* Dies war ein Test um die Daten eine Ebene höher zu betrachten. Wurde aber nicht gebraucht.
&nbsp;&nbsp;getLevel1(): Observable<Level1[]> {
&nbsp;&nbsp;&nbsp;&nbsp;//var content:Level1[]=[];
&nbsp;&nbsp;&nbsp;&nbsp;var content: any;
&nbsp;&nbsp;&nbsp;&nbsp;content = this.http.get<Level1[]>(this.accountsUrl)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.pipe(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tap(_ => this.log('fetched accounts')),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catchError(this.handleError<Level1[]>('getLevel1', []))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);
&nbsp;&nbsp;&nbsp;&nbsp;this.log("Der Inhalt von Content ist:" + JSON.stringify(content));

&nbsp;&nbsp;&nbsp;&nbsp;return JSON.parse(content[0]);
&nbsp;&nbsp;} */

&nbsp;&nbsp;getAccount(id: number): Observable<Account> {
&nbsp;&nbsp;&nbsp;&nbsp;const url = `${this.accountUrl}/${id}`;
&nbsp;&nbsp;&nbsp;&nbsp;return this.http.get<Account>(url).pipe(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tap(_ => this.log(`fetched account id=${id}`)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catchError(this.handleError<Account>(`getAccount id=${id}`))
&nbsp;&nbsp;&nbsp;&nbsp;);
&nbsp;&nbsp;}

&nbsp;&nbsp;//////// Save methods //////////

&nbsp;&nbsp;/** POST: add a new account to the server */
&nbsp;&nbsp;addAccount(account: Account): Observable<any> {
&nbsp;&nbsp;&nbsp;&nbsp;const body = JSON.stringify(account);
&nbsp;&nbsp;&nbsp;&nbsp;const url = `${this.accountUrl}`;
&nbsp;&nbsp;&nbsp;&nbsp;console.log("Der Body vom Post lautet:" + body);
&nbsp;&nbsp;&nbsp;&nbsp;return this.http.post(url, body, this.httpOptions)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.pipe(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catchError((err) => {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.error(err);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw err;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;))
&nbsp;&nbsp;}

&nbsp;&nbsp;/** DELETE: delete the account from the server */
&nbsp;&nbsp;deleteAccount(id: number): Observable<Account> {
&nbsp;&nbsp;&nbsp;&nbsp;const url = `${this.deleteUrl}/${id}`;
&nbsp;&nbsp;&nbsp;&nbsp;return this.http.delete<Account>(url, this.httpOptions).pipe(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tap(_ => this.log(`deleted account id=${id}`)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catchError(this.handleError<Account>('deleteAccount'))
&nbsp;&nbsp;&nbsp;&nbsp;);
&nbsp;&nbsp;}

&nbsp;&nbsp;/**
&nbsp;&nbsp;&nbsp;* Handle Http operation that failed.
&nbsp;&nbsp;&nbsp;* Let the app continue.
&nbsp;&nbsp;&nbsp;*
&nbsp;&nbsp;&nbsp;* @param operation - name of the operation that failed
&nbsp;&nbsp;&nbsp;* @param result - optional value to return as the observable result 
&nbsp;&nbsp;&nbsp;* */

&nbsp;&nbsp;private handleError<T>(operation = 'operation', result?: T) {
&nbsp;&nbsp;&nbsp;&nbsp;return (error: any): Observable<T> => {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// TODO: send the error to remote logging infrastructure
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.error(error); // log to console instead

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// TODO: better job of transforming error for user consumption
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.log(`${operation} failed: ${error.message}`);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Let the app keep running by returning an empty result.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return of(result as T);
&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;}

&nbsp;&nbsp;/** Log a AccountService message with the MessageService */
&nbsp;&nbsp;private log(message: string) {
&nbsp;&nbsp;&nbsp;&nbsp;this.messageService.add(`AccountService: ${message}`);
&nbsp;&nbsp;}
}

Conclusion:

Now we are able to send CRUD (Create, Read, Update and Delete) requests to a real API Server via the Angular frontend.
 
https://www.thebmc.co.uk/Handlers/ArticleImageHandler.ashx?id=7700&index=0&w=605&h=434

origin

The next step will be to include other API servers from the internet and combine it with my accounts. I want to see the last logindate in Hive of a given username.
So stay tuned!!!

Regards, Achim

Comments

Sort byBest
AI
Waivio AI Assistant
How can I help you today?