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