Restore form data after re-routing

102 Views Asked by At

I have form in which form Control's are dependent on one another. The flow:

  1. On input key down, dropdown values(universities) will be fetched.
  2. On selecting the dropdown value, Next dropdown values(courses) will fetched.

I just added filter function. But actual values will be fetched from api's.

enter image description here

component B

import { HttpClient, HttpClientJsonpModule } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { browserRefresh } from '../app.component';

@Component({
  selector: 'app-page-b',
  templateUrl: './page-b.component.html',
  styleUrls: ['./page-b.component.css'],
})
export class PageBComponent implements OnInit {
  contactForm!: FormGroup;
  universities: any = [
    {
      id: '1',
      name: 'Columbia',
    },
    {
      id: '2',
      name: 'Oxford',
    },
    {
      id: '3',
      name: 'Cathedral',
    },
  ];
  courses: any = [
    {
      universityId: '1',
      name: 'English',
    },
    {
      universityId: '1',
      name: 'Pythin',
    },
    {
      universityId: '3',
      name: 'Literature',
    },
    {
      universityId: '3',
      name: 'Commerce',
    },
    {
      universityId: '2',
      name: 'Physics',
    },
    {
      universityId: '2',
      name: 'Java',
    },
  ];
  filteredUniversities = [];
  filteredCourses = [];
  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    console.log('refreshed?:', browserRefresh);
    this.contactForm = this.fb.group({
      name: new FormControl(),
      university: new FormControl(),
      course: new FormControl(),
    });
  }

  Search(event: any) {
    const searchItem = event.target.value;
    this.filteredUniversities = this.universities.filter((item) => {
      return item.name.toLowerCase().includes(searchItem.toLowerCase());
    });
  }

  filterCourses(uId) {
    this.filteredCourses = this.courses.filter((item) => {
      return item.universityId == uId;
    });
  }
}

I need to restore this form data. When I navigate/reroute back to another component(ex from component A). Along with selected values, the dropdown values should be restored like user selected before router exit.

I am distinguishing component/page re-load & re-route by using router events:

 constructor(private router: Router) {
    this.subscription = router.events.subscribe((event) => {
        if (event instanceof NavigationStart) {
          browserRefresh = !router.navigated;
        }
    });
  }

1. I am aware I can store form data in local Storage/services, then can bind the data to the form fields. But, I need to restore previously stored dropdown values.

2. I don't want to loose the component instance. Any hack to achieve this.

Stackblitz link.

1

There are 1 best solutions below

0
Andrey Babitsyn On

I've forked your example from stackblitz: https://stackblitz.com/edit/angular-material-reactive-forms-validation-yc2y1u

You still need a service to store the form data somewhere. Services are often singletons so, the can keep the data during the application lifecycle. Currently my form-data.service simply stores the data in RxJS subject but in the future you can extend it to store form drafts using your API.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class FormDataService {
  private formData = new BehaviorSubject<any>({});
  currentFormData = this.formData.asObservable();

  constructor() {}

  updateFormData(data: any) {
    this.formData.next(data);
  }
}

    import { HttpClient, HttpClientJsonpModule } from '@angular/common/http';
    import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
    import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
    import { Subscription } from 'rxjs';
    import { filter, first } from 'rxjs/operators';
    import { browserRefresh } from '../app.component';
    import { FormDataService } from './form-data.service';

@Component({
  selector: 'app-page-b',
  templateUrl: './page-b.component.html',
  styleUrls: ['./page-b.component.css'],
})
export class PageBComponent implements OnInit, OnDestroy {
  contactForm!: FormGroup;
  universities: any = [
    {
      id: '1',
      name: 'Columbia',
    },
    {
      id: '2',
      name: 'Oxford',
    },
    {
      id: '3',
      name: 'Cathedral',
    },
  ];
  courses: any = [
    {
      universityId: '1',
      name: 'English',
    },
    {
      universityId: '1',
      name: 'Pythin',
    },
    {
      universityId: '3',
      name: 'Literature',
    },
    {
      universityId: '3',
      name: 'Commerce',
    },
    {
      universityId: '2',
      name: 'Physics',
    },
    {
      universityId: '2',
      name: 'Java',
    },
  ];
  filteredUniversities = this.universities;
  filteredCourses = this.courses;
  formDataSubscription: Subscription;

  constructor(
    private fb: FormBuilder,
    private formDataService: FormDataService,
    private cdRef: ChangeDetectorRef
  ) {}

  ngOnInit() {
    console.log('refreshed?:', browserRefresh);
    this.contactForm = this.fb.group({
      name: new FormControl(''),
      university: new FormControl({}),
      course: new FormControl({}),
    });

    this.formDataService.currentFormData
      .pipe(
        filter((data) => Object.keys(data).length > 0),
        first()
      )
      .subscribe((formData) => {
        if (formData.name) {
          this.contactForm
            .get('name')
            .setValue(formData.name, { emitEvent: false });
        }
        // Match and set the university value
        const matchedUniversity = this.filteredUniversities.find(
          (u) => u.id === formData.university.id
        );
        this.contactForm
          .get('university')
          .setValue(matchedUniversity, { emitEvent: false });

        // Match and set the course value
        const matchedCourse = this.filteredCourses.find(
          (c) =>
            c.universityId === formData.course.universityId &&
            c.name === formData.course.name
        );
        this.contactForm
          .get('course')
          .setValue(matchedCourse, { emitEvent: false });
        this.contactForm.updateValueAndValidity();
        // this.cdRef.detectChanges();
      });

    this.formDataSubscription = this.contactForm.valueChanges.subscribe(
      (formData) => {
        this.formDataService.updateFormData(formData);
      }
    );
  }

  ngOnDestroy() {
    this.formDataSubscription.unsubscribe();
  }

  Search(event: any) {
    const searchItem = event.target.value;
    this.filteredUniversities = this.universities.filter((item) => {
      return item.name.toLowerCase().includes(searchItem.toLowerCase());
    });
  }

  filterCourses(uId) {
    this.filteredCourses = this.courses.filter((item) => {
      return item.universityId == uId;
    });
  }
}