Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
39
rated 0 times [  45] [ 6]  / answers: 1 / hits: 5143  / 4 Years ago, tue, march 24, 2020, 12:00:00

I am writing an Angular9 app with a use case where I am buffering data across 3 pages (with forms taking data input).



Then, on the 4th page, I display a summary of the details gathered across the 3 pages as read only text.



I have implemented the data buffering using the following article:



https://www.infragistics.com/community/blogs/b/infragistics/posts/simplest-way-to-share-data-between-two-unrelated-components-in-angular



Which uses the BehaviorSubject



Below is my data service logic:



    import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, BehaviorSubject, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
import { UserResponse } from './user-response';

@Injectable({
providedIn: 'root'
})

export class UserService {

//Details Page 1
public salutation: BehaviorSubject<string>;
public firstName: BehaviorSubject<string>;

// Base url
baseurl = 'https://localhost:8080/rest';

constructor(private http: HttpClient) { }

// Http Headers
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}

// POST
createUser(data): Observable<UserResponse> {
alert('user - details = '+ data);
return this.http.post<UserResponse>(this.baseurl + '/user', JSON.stringify(data), this.httpOptions)
.pipe(
retry(1),
catchError(this.errorHandler)
)
}

// Error handling
errorHandler(error) {
let errorMessage = '';
if(error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}

}


Below is my first view component:



import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { UserService } from '../service/user.service';
import { BehaviorSubject } from 'rxjs';

@Component({
selector: 'app-details1',
templateUrl: './details1.component.html',
styleUrls: ['./details1.component.css']
})
export class Details1Component implements OnInit {
salutation: string;
firstName: string;

constructor(private userService: UserService) { }

ngOnInit(): void {
}

goToDetails2(form : NgForm) {
this.userService.salutation = new BehaviorSubject(form.value.salutation);
this.userService.firstName = new BehaviorSubject(form.value.firstName);
}

}


Below is a snippet of my final/summary view component



import { Component, OnInit} from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../service/user.service';

@Component({
selector: 'app-summary',
templateUrl: './summary.component.html',
styleUrls: ['./summary.component.css']
})

export class SummaryComponent implements OnInit {

salutation: string;
firstName: string;

constructor(
private router: Router,
public userService: UserService
) {
}

ngOnInit(): void {
this.userService.salutation.subscribe(c => { this.salutation = c; });
this.userService.firstName.subscribe(c => { this.firstName = c; });
}

}


On the final page my html excerpts is as follows:



<form #detailsForm4=ngForm>
<div class=govuk-form-group>
<table class=govuk-table>
<thead class=govuk-table__head>
<tr class=govuk-table__row></tr>
</thead>
<tbody class=govuk-table__body align=left>
<tr class=govuk-table__row>
<th scope=row class=govuk-table__header>Salutation</th>
<td class=govuk-table__cell> {{salutation}} </td>
<td class=govuk-table__cell> </td>
</tr>
<tr class=govuk-table__row>
<th scope=row class=govuk-table__header>First name</th>
<td class=govuk-table__cell> {{firstName}} </td>
<td class=govuk-table__cell> </td>
</tr>


The data is displayed on the summary page as captured on the forms in pages 1-3,
BUT the data is lost on page refresh or when my machine hibernates etc



I have read about local and session storage persistence and used it since the angularjs 1.x days.
Ages ago, I was actually surprised when the data disappeared on my summary screen!



I guess that an ideal solution will involve storing the data in client side storage when the user navigates away from the relevant form, then retrieving the data on the summary page.



I guess, when the summary page is refreshed or displayed for the first time,
I will check storage for the data and display if it exist.
If storage is empty, then I will be using data from previous page at runtime.



Please, I need help with a best practice and stable solution, which is cross browser friendly and intelligent for a commercial application.



Also, an approach that fits into Angular9 BehaviorSubject - rxjs stack OR Any recent features, because I know Angular has evolved since AngularJs 1.x ?



The summary details is only changed when the user navigates back to any of the pages 1-3 to change the form details.



I will appreciate any code snippet or reference to keep the data on the summary page.



Thanks a lot.


More From » angular

 Answers
23

I will propose you to use localStorage within your SummaryComponent within ngOnDestroy lifecycle hook. It will always set your data within localStorage before component will be destroyed due to refresh. Then when you are trying to retrieve data from the service, if it will be empty try to get it from localStorage.



export class SummaryComponent implements OnInit, OnDestroy {

salutation: string;
firstName: string;

constructor(
private router: Router,
public userService: UserService
) {
}

ngOnInit(): void {
this.userService.salutation.subscribe(c => { this.salutation = c || localStorage.get('salutation'); });
this.userService.firstName.subscribe(c => { this.firstName = c || localStorage.get('firstName'); });
}

ngOnDestroy(): void {
localStorage.setItem('salutation', this.salutation));
localStorage.setItem('firstName', this.firstName));
}

}


Similar implementation you can do in the component where this values are set and you expect that user can refresh the page before go to the next page.



EDIT



Sorry, you are right that ngOnDestory will not trigger on page refresh to do that you will need to handle window:beforeunload.



  @HostListener('window:beforeunload', ['$event'])
beforeunload($event: Event): void {
// set localStorage here
}


Going forward with your solution, the best option to set items to the localStorage is the place where you set values to the service. It's probably goToDetails2 method.



goToDetails2(form : NgForm) {
this.userService.salutation = new BehaviorSubject(form.value.salutation);
this.userService.firstName = new BehaviorSubject(form.value.firstName);
localStorage.setItem('salutation', form.value.salutation));
localStorage.setItem('firstName', form.value.firstName));
}


EDIT-2 to your problem described in the comment



I will propose you to initialize your BehaviorSubject directly inside of the service.



@Injectable({
providedIn: 'root'
})

export class UserService {

//Details Page 1
public salutation: BehaviorSubject<string> = new BehaviorSubject('');
public firstName: BehaviorSubject<string> = new BehaviorSubject('');


And then within your Details1Component set values on them using next



goToDetails2(form : NgForm) {
this.userService.salutation.next(form.value.salutation);
this.userService.firstName.next(form.value.firstName);
localStorage.setItem('salutation', form.value.salutation));
localStorage.setItem('firstName', form.value.firstName));
}

[#4386] Sunday, March 22, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
loganl

Total Points: 424
Total Questions: 86
Total Answers: 112

Location: Zimbabwe
Member since Thu, Jul 21, 2022
2 Years ago
loganl questions
;