Home > Back-end >  Using same component for new and existing form records without ngChanges
Using same component for new and existing form records without ngChanges

Time:01-01

I have 2 components, a parent and a child component. Between them they can serve urls for new and existing records. ie applications/add AND applications/:guid

I currently have this for my parent component:

HTML:

<app-university-applications-form
    [existingData]="(existingFormData$ | async)!"
></app-university-applications-form>

TS:

existingFormData$: Observable<UniversityApplicationsDetail> = this.activatedRoute.params.pipe(
    map(params => params['id']),
    filter(id => !!id),
    switchMap((id: string) => this.httpService.getUniversityApplicationsDetail(id))
  );

And the following for my child component:

TS

@Input() existingData!: UniversityApplicationsDetail; //Using ! here is the "non-null assertion operator"

ngOnChanges(changes: SimpleChanges){
    if ((this.existingData !== null)){
      this.title.setTitle("Update University Application");
      this.populateForm();
    }
  }

And this works all fine. But I want to avoid using ngOnChanges and do everything from ngOnit in the child component.

If I change the parent component to:

html

<app-university-applications-form
    *ngIf="(existingFormData$ | async)! as detail"
    [existingData]="detail"
></app-university-applications-form>

And the child component to:

TS

ngOnInit(): void {
    this.title.setTitle("New University Application");
    this.createForm();

    if ((this.existingData !== null)){
      this.title.setTitle("Update University Application");
      this.populateForm();
    }
  }

Then the form displays when on existing record mode but not on new record mode. How can I fix this?

CodePudding user response:

Solution 1

You could create a second observable isNewRecord$ that resolves to true whenever id is null. And if isNewRecord$ resolves to true you trigger the rendering of app-university-applications-form without passing a value to the input-property [existingData].

The Parent TS:

id$ = this.activatedRoute.params.pipe(
    map(params => params['id'])
);

// Will emit a value whenever an id is provided:
existingFormData$ = this.id$.pipe(
    filter(id => !!id),
    switchMap((id: string) => this.httpService.getUniversityApplicationsDetail(id))
);

// Will be true whenever *no* id is provided:
isNewRecord$ = this.id$.pipe(
    filter(id => !id),
    map(() => true)
);

The Parent HTML:

<app-university-applications-form
    *ngIf="(existingFormData$ | async)! as detail"
    [existingData]="detail">
</app-university-applications-form>

<app-university-applications-form
    *ngIf="isNewRecord$  | async">
</app-university-applications-form>

Solution 2

If you want just one app-university-applications-form in your html, you could do the following:

  • In case there is an ID, pass the object that is returned from the backend
  • In case there is no ID, you also pass an object of the same type, but let's say with the property id: undefined. Therefore you will know in your child-component, if id === undefined, it must be a new record.

The corresponding Parent TS might look as follows:

existingFormData$ = this.activatedRoute.params.pipe(
    map(params => params['id']),
    switchMap((id: string) => !!id ?
      this.httpService.getUniversityApplicationsDetail(id) : of({ id: undefined }))
);

Parent HTML:

<app-university-applications-form
    *ngIf="(existingFormData$ | async)! as detail"
    [existingData]="detail">
</app-university-applications-form>

Child TS:

ngOnInit(): void {
    this.title.setTitle("New University Application");
    this.createForm();
    
    // Check if a particular property is set, to determine whether its a new record:
    if (this.existingData.id) {
      this.title.setTitle("Update University Application");
      this.populateForm();
    }
}
  • Related