Quarkus + Angular with Keycloak — Pt3 Final
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:
- Create quote-list
- 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 afindAll
method that calls the backend REST API athttp://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.html
and 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! ❤️️