Quarkus + Angular with Keycloak — Pt3 Final

Ricardo Mello
ITNEXT
Published in
5 min readMar 15, 2023

--

Discover more on YouTube: If you’re looking for video content on technology, programming, and other topics, check out my YouTube channel! Subscribe to stay updated and learn more about these subjects.

Welcome back, my fellow developer friend! You’ve made it to the final part of the epic journey of building our daily-quotes application! If you missed out on the previous part, don’t fret and click here to catch up on the fun-filled action.

In this last part of the series, we’ll be focusing on establishing a smooth communication between our front-end and back-end. Let’s dive in and finish this adventure once and for all! 🚀

Angular Application

Let’s modify our Angular application by generating some components that will allow us to add and list quotes. We’ll divide it into two groups:

  1. Create quote-list
  2. Create quote-add

Let’s gooooo!!

Create quote-list

  • Open Generating Quote class:
$ ng generate class quote
  • Generating Service:
$ ng generate service quote-service
  • Generating quote-list component:
$ ng generate component quote-list

Implementing components

In this step we ‘ll change the generated components.

  • Open quote.ts and add the following fields:
export class Quote {
message?: string;
author?: string;
}
  • Open quote-service.service.ts and add the following code to create a findAll method that calls the backend REST API at http://localhost:8080/quote:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Quote } from 'src/app/model/quote';

@Injectable({
providedIn: 'root'
})
export class QuoteService {
private quoteUrl: string;
constructor(private http: HttpClient) {
this.quoteUrl = 'http://localhost:8080/quote';
}
public findAll(): Observable<Quote[]> {
return this.http.get<Quote[]>(this.quoteUrl);
}
}
  • Open quote-list.component.ts and inject and implement the service as shown below:
import { Component, OnInit } from '@angular/core';
import { Quote } from '../model/quote';
import { QuoteService } from '../services/quote/quote-service.service';

@Component({
selector: 'app-quote-list',
templateUrl: './quote-list.component.html',
styleUrls: ['./quote-list.component.scss']
})
export class QuoteListComponent implements OnInit {

quotes?: Quote[];
constructor(private quoteService: QuoteService) {
}
async ngOnInit() {
this.quoteService.findAll().subscribe(data => {
this.quotes = data;
});
}
}
  • Open quote-list.component.html and modify to be like this:
<div class="container">
<div class="row" *ngFor="let quote of quotes">
<div class="col-sm-12">

<blockquote class="blockquote">
<p class="mb-0">
{{quote.message}}
</p>
<footer class="blockquote-footer">
{{quote.author}}
</footer>
</blockquote>
</div>
</div>
</div>
  • Open app.module.ts declare QuoteListComponent and importing HttpClientModule:
@NgModule({
declarations: [
AppComponent,
QuoteListComponent,
NavbarComponent,
AdminComponent,
],
imports: [
BrowserModule,
CommonModule,
AppRoutingModule,
BrowserAnimationsModule,
MatToolbarModule,
MatButtonModule,
MatMenuModule,
MatIconModule,
KeycloakAngularModule,
HttpClientModule,
],
  • Adding QuoteListComponent route on app.routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { QuoteAddComponent } from './component/quote-add/quote-add.component.component';
import { QuoteListComponent } from './component/quote-list/quote-list.component';

const routes: Routes = [
{ path: 'home', component: AppComponent },
{ path: 'quotes', component: QuoteListComponent },
{ path: 'add', component: QuoteAddComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
  • Finally, we have to enable http.cors on application.properties in Quarkus service:
quarkus.http.cors=true
%dev.quarkus.http.cors.origins=/.*/

Very good! 😍 We have finished implementing the quote listing route. Now we are able to get quotes from the backend.

Create quote-add

  • Generating quote-add component:
$ ng generate component quote-add

Implementing components

In this step we ‘ll change the generated components.

  • Open quote-add.component.ts and include the following code:
import {Component, Inject} from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import { Quote } from 'src/app/model/quote';
import { QuoteService } from 'src/app/services/quote/quote-service.service';

export interface DialogData {
message: string;
author: string;
}

@Component({
selector: 'app-quote-add',
templateUrl: './quote-add.component.html',
styleUrls: ['./quote-add.component.scss']
})
export class QuoteAddComponent {
constructor(
public dialogRef: MatDialogRef<QuoteAddComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData,
private quoteService: QuoteService
) {}
onNoClick(): void {
this.dialogRef.close();
}
}
  • Open quote-add.component.html and modify:
<div class="form">
<h1 mat-dialog-title>Create new Quote</h1>
<div mat-dialog-content>
<mat-form-field class="field-author" appearance="outline">
<mat-label>Author</mat-label>
<input matInput [(ngModel)]="data.author">
</mat-form-field>
<mat-form-field class="field-message" appearance="outline">
<mat-label>Message</mat-label>
<textarea matInput [(ngModel)]="data.message"></textarea>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="buttons">
<button mat-button (click)="onNoClick()">Cancel</button>
<button mat-button [mat-dialog-close]="data">Create</button>
</div>
  • Open quote-add.component.scss and include the following code:
.form {
max-width: 500px;
width: 100%;
margin: 10px;
}

.field-author {
align-items: center;
margin: 10px;
}
.field-message {
width: 400px;
margin: 5px;
}
.buttons {
align-items: center;
background-color: deepskyblue;
margin: 10px;
}
  • Open navbar.component.ts and change the content. It should be like this:
import { Component, OnInit } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
import { MatDialog } from '@angular/material/dialog';
import { QuoteAddComponent } from '../quote-add/quote-add.component';
import { QuoteService } from 'src/app/services/quote/quote-service.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})

export class NavbarComponent implements OnInit {
constructor(
private readonly keycloak: KeycloakService,
public dialog: MatDialog,
public quoteService: QuoteService,
public router: Router
) { }
public hasAdminRole: boolean = false;
message?: string;
author?: string;

ngOnInit(): void {
this.hasAdminRole = this.keycloak.getUserRoles().includes('admin');
}

public async logout() {
this.keycloak.logout();
}
public add() {
const dialogRef = this.dialog.open(QuoteAddComponent, {
data: {message: this.message, author: this.author},
})
dialogRef.afterClosed().subscribe(async result => {
(await this.quoteService.save(result)).subscribe( result =>
this.redirectTo('quotes')
);
}
);
}
redirectTo(uri:string){
this.router.navigateByUrl('/', {skipLocationChange: true}).then(()=>
this.router.navigate([uri]));
}
}
  • Open navbar.component.htmland include the add method to new quote button:
<button mat-menu-item (click)="add()">New Quote</button>
  • Let's create our save method on quote-service.service.ts:
async save(quote: Quote): Promise<Observable<Quote>>{
return this.http.post<Quote>(this.quoteUrl, quote);
}

Very good 😍! We have finished implementing our quote add route. Now, we are able to add new quotes.

Conclusion

Great job! We have completed the development of our daily-quotes application by implementing communication between the frontend and backend protected by OIDC with Keycloak. I appreciate your effort and dedication throughout the three parts of this article.

The complete source code for this project is available on my GitHub. Please feel free to suggest, comment, or contribute to this project.

Thank you for reading and happy coding! ❤️️

--

--

Senior Software Engineer, member of the MongoDB Community Creator passionate about travel and football. Oracle Certified Associate, Java SE 8 Programmer