Search code examples
angularionic-frameworkgoogle-cloud-firestoreswiper.js

How can I force the reload of parent component from its child component?


This app was written using Angular 17 (standalone) and Ionic 7. Tab1Page ("cards") is the parent and ItemCardComponent is its child (a Swiper carousel of ion-cards which values are recovered from a Firestore Database):

export class Tab1Page {
 slides: itemData[] = [];

 constructor(public itemService: ItemService) {
    this.ionViewDidEnter();
 }

 ionViewDidEnter() {
  this.itemService.getThings().subscribe((items: itemData[]) => {
   const extra: itemData = {
        user: '',
        name: '',
        picture: ''
   }
   items.unshift(extra)
   this.slides = items;
  });
 }
}

From ItemService:

getThings(): Observable<itemData[]> {
    const itemsRef = collection(this.firestore, 'userItems');  
    const q = query(itemsRef, where('user', '==', this.auth.currentUser!.uid) );
    return collectionData(q, { idField: 'id'}) as Observable<itemData[]>;
  }

NOTE: First card ('extra') is always blank and it's used for data input. first card of the Swiper carousel

When a new card is saved ("setThing" is called from the child "ItemCardComponent"), data is stored as a new item to 'userItems' (Firestore collection). From ItemService:

setThing(thingUnit: itemData) {
  thingUnit.user = this.auth.currentUser?.uid;
  const itemsRef = collection(this.firestore, 'userItems');
  return addDoc(itemsRef, thingUnit); 
}

I think I get to a correct diagnose of the issue, but I'm stuck on what to do to solve it.

Everything works fine when it come to save (and recover) data from Firestore. Except for the carousel view, which goal was to be updated in real time, but currently requires a manual refresh of Tab1Page to show the new slide. Obviously, It doesn't update because the saving event (that occurs in child) doesn't trigger any kind of change to the slide array (that relies on the load/reload of parent). The calls that updates this.slides array with the values from "items" will only happen when the constructor and ionViewDidEnter() run.

What commands should I use to force a refresh of Tab1Page (parent), taking into account that I'll need to run it from the ItemCardComponent (child), together with the card saving/editing functions?

I guess if that should be other solutions to this, because I understand that this current code implies too much calls to Firebase and that's not the optimal scenario.


Solution

  • This is how I got the app to have "acceptable" behavior for the moment:

    When new items are saved, child component emits the event (as it was well suggested by @SwissCodeMen) to call a function in the parent component .

    Decorator declaration:

    @Output() reloadParentEmitter = new EventEmitter<boolean>();
    

    Triggering the event from the child's "saveItem" function:

    this.reloadParentEmitter.emit(true);
    

    "Capturing" the event from the parent side (tab1.page.html):

    <app-item-card (reloadParentEmitter)="reloadItems($event)"></app-item-card>
    

    And, finally, running the "routing approach" from the function that's called in "tab1.page.ts" (parent):

    reloadItems(reloading: boolean){
    this.router.navigateByUrl('blankCanvas', { skipLocationChange: true }).then(() => {
      this.router.navigate(['reload']);
    });}
    

    As I just wanted a reload of the 'ion-tab' (parent) where the 'app-item-card' (child) is nested, all that I needed was a quick navigation to any other path and back again to the original one. So I generated a blank page with the same background of the entire app and pointed my first jump to it:

    this.router.navigateByUrl('blankCanvas', { skipLocationChange: true })
    

    And than, back to the original path, where data is already updated:

    this.router.navigate(['reload'])
    

    To achieve the correct routes I edited path values in "app.routes.ts":

    {path: 'reload', loadChildren: () => import('./pages/private/tabs/tabs.routes').then((m) => m.routes)},
    {path: 'blankCanvas', loadComponent: () => import('./pages/private/shared/components/blankCanvas/blankCanvas.page').then( m => m.BlankCanvasPage)}
    

    And, in "tabs.routes.ts", I added a copy of the original tab path, renamed to 'reload':

    {path: 'reload', redirectTo: '/tabs/tab1', pathMatch: 'full'}
    

    As I said, that's none but the "acceptable" solution I got to, right now. But I will keep investigating and I'm sure I'll find a better approach to it.