Quarkus + Angular with Keycloak — Pt3

ricardohsmello

Ricardo Mello

Posted on March 23, 2023

Quarkus + Angular with Keycloak — Pt3

On this article, we 'll finish the development of our daily-quotes application. If you haven’t seen the second part, click here. The main goal of this part is implement a communication between the frontend and the backend.

Angular Application

Let’s change our Angular application and generate some components that will add and list quotes. Let’s divide it into two groups:

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

Create quote-list

Generating Quote class:

$ ng generate class quote
Enter fullscreen mode Exit fullscreen mode

Generating Service:

$ ng generate service quote-service
Enter fullscreen mode Exit fullscreen mode

Generating quote-list component:

$ ng generate component quote-list
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Open quote-service.service.ts and create a findAll method and define the backend restAPI 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Open quote-list.component.ts and inject and implement service:

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;
    });
  }

}
Enter fullscreen mode Exit fullscreen mode

Open quote-list.component.html must 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>   
Enter fullscreen mode Exit fullscreen mode

Declaring QuoteListComponent and import HttpClientModule on app.module.ts:

@NgModule({
  declarations: [
    AppComponent,
    **QuoteListComponent**,
    NavbarComponent,
    AdminComponent,
  ],
  imports: [
    BrowserModule,
    CommonModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatToolbarModule,
    MatButtonModule,
    MatMenuModule,
    MatIconModule,
    KeycloakAngularModule,
    **HttpClientModule**,
  ],
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

Finally, we have to enable http.cors on application.properties in Quarkus backend:

quarkus.http.cors=true
%dev.quarkus.http.cors.origins=/.*/
Enter fullscreen mode Exit fullscreen mode

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

Create quote-add

Generating quote-add component:

$ ng generate component quote-add
Enter fullscreen mode Exit fullscreen mode

Implementing components
In this step we 'll change the generated components.

Open quote-add.component.ts

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();
  }

}
Enter fullscreen mode Exit fullscreen mode

Open quote-add.component.html

<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>
Enter fullscreen mode Exit fullscreen mode

Open quote-add.component.scss

.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;
}
Enter fullscreen mode Exit fullscreen mode

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]));
 }

}
Enter fullscreen mode Exit fullscreen mode

Open navbar.component.html and include the add method to new quote button:

<button mat-menu-item (click)="add()">New Quote</button>
Enter fullscreen mode Exit fullscreen mode

Lets create our add http 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.

Image description


Conclusion
On this third and last part we finished our daily-quotes application by implementing communication between the frontend and backend protected by OIDC with keycloak.

Well done guys, the whole project is available at my gitHub.

Feel comfortable to suggest, comment and contribute with this project.

Thanks ❤️

💖 💪 🙅 🚩
ricardohsmello
Ricardo Mello

Posted on March 23, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related