We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

Hierarchical grid child grid firebase realtimedb datasource

https://ej2.syncfusion.com/angular/documentation/grid/hierarchy-grid

I am trying to use this example of a hierarchy grid but I struggling to understand how to set datasource for child grid

@Component({
    selector: 'app-root',
    template: `
                    
                        
                        
                        
                        
                    
                
                `,
    providers: [DetailRowService, EditService, ToolbarService]
})
export class AppComponent implements OnInit {

    public pData: object[];
    public childGrid: GridModel = {
        dataSource: data,
        queryString: 'EmployeeID',
        toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
        editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true },
        columns: [
            { field: 'OrderID', headerText: 'Order ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
            { field: 'EmployeeID', headerText: 'Employee ID', textAlign: 'Right', allowEditing: false, width: 120 },
            { field: 'ShipCity', headerText: 'Ship City', width: 150 },
            { field: 'ShipName', headerText: 'Ship Name', width: 150 }
        ],
        actionBegin(args: AddEventArgs) {
            if (args.requestType === 'add') {
                // `parentKeyFieldValue` refers to the queryString field value of the parent record.
                const EmployeeID = 'EmployeeID';
                (args.data as object)[EmployeeID] = this.parentDetails.parentKeyFieldValue;
            }
        }
    };
    @ViewChild('grid') public grid: GridComponent;

    ngOnInit(): void {
        this.pData = employeeData;
    }
}


    I am accustomed of setting data source for the main grid as follows and that works fine:


    <ejs-grid #mainGrid (actionComplete)='actionCompleteMain($event)' height="700" allowResizing="true" [childGrid]='childGrid' [allowSorting]="true" [editSettings]='editSettings' [toolbar]='toolbar' [allowFiltering]='true' [filterSettings]="filterOption">
    <e-columns >
    <e-column field='carReg' headerText='Car Reg' width='120' >e-column>
    <e-column field='carMake' headerText='Car Make/Model' width='150' >e-column>
    <e-column field='clientName' headerText='Client Name' width='150' >e-column>
    <e-column field='adress' headerText='Adress' width='150' >e-column>
    <e-column field='mobile' headerText='Mobile' width='150' >e-column>
    <e-column field='email' headerText='Email' width='150' >e-column>
    <e-column field='milage' headerText='Milage' width='150' >e-column>
    <e-column field='vat' headerText='Vat' width='150' >e-column>
    <e-column field='workers' headerText='Workers' width='150' >e-column>
    <e-column field='status' headerText='Status' width='150' >e-column>
    e-columns>
    ejs-grid>


    export class MainComponent {
    constructor( public firebase: AngularFireDatabase,
    private firebasedb: FirebaseService,) {

    firebase
    .list('/main')
    .valueChanges()
    .subscribe((users) => {
    this.mainGrid.dataSource = users; //intial data binding to grid
    });

    firebase
    .list('/main')
    .snapshotChanges()
    .subscribe((users) => {
    this.mainGrid.dataSource = users; // sync server data changes to grid
    });

    }


    @ViewChild('mainGrid')
    public mainGrid: GridComponent;
    public childGrid: GridModel = {
    dataSource: "",
    queryString: 'key',
    toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
    editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true },
    // columns: [
    // { field: 'OrderID', headerText: 'Order ID', isPrimaryKey: true, textAlign: 'Right', width: 120 },
    // { field: 'EmployeeID', headerText: 'Employee ID', textAlign: 'Right', allowEditing: false, width: 120 },
    // { field: 'ShipCity', headerText: 'Ship City', width: 150 },
    // { field: 'ShipName', headerText: 'Ship Name', width: 150 }
    // ],
    actionBegin(args: AddEventArgs) {
    if (args.requestType === 'add') {
    // `parentKeyFieldValue` refers to the queryString field value of the parent record.
    const EmployeeID = 'EmployeeID';
    (args.data as object)[EmployeeID] = this.parentDetails.parentKeyFieldValue;
    }
    }
    };

    public actionCompleteMain(args: any): void {
    switch (args.requestType) {
    case 'save':
    this.firebasedb.updateMain(JSON.parse(JSON.stringify(args.data)));
    break;
    case 'delete':
    args.data.forEach((row: any) => {
    this.firebasedb.removeMain(row.key);
    });
    break;
    }
    }


    ngOnInit(): void {
    }

    }



    I think I would like to store the data of child under the parent data node in firebase like this "childEntries":


    How could I plug this into child grid dataSource?


    7 Replies

    VS Vikram Sundararajan Syncfusion Team April 12, 2023 01:33 PM UTC

    Hi Frank Alberts,


    Greetings from syncfusion support,


    Thank you for reaching out to us. We have reviewed your query about setting the datasource for the child grid in a hierarchy grid. After analyzing your request, we have confirmed that the hierarchy grid does not support using the same data source for both parent and child grids due to the foreign key column's functionality. However, we would like to suggest using the detailTemplate to store the child grid under the parent grid to fulfill your requirements. We have prepared the sample based on your requirement.


    We have included a code snippet below that you can use to achieve your requirement.


    [app.component.ts] 

     

     detailDataBound(eDetailDataBoundEventArgs) {

          var index=parseInt((e as any).detailElement.children[0].parentElement.parentElement.previousSibling.getAttribute('data-rowindex'))

        let detail = new Grid({

          dataSource:  summaryRowData[index]['children'],

          columns: [

            {field:'orderID'headerText:'orderID'width:'70' ,textAlign:'Right'},

            { field: 'TotalCosts'headerText: 'TotalCosts'width: 140 },

            { field: 'UnitWeight'headerText: 'UnitWeight'width: 150 },

          ],

        });


    Sample: https://stackblitz.com/edit/angular-l8ye2l?file=src%2Fapp.component.ts


    If you require further assistance, please do not hesitate to contact us. We are always here to help you.


    Regards,

    Vikram S




    JB Jonas Blazinskas April 12, 2023 02:35 PM UTC

    Hi Vikram,

    Thank you for your help so far! But I am struggling to understand how to set the datasource as the one from firebase:

    export class MainComponent {

      constructor(    public firebase: AngularFireDatabase,
        private fns: AngularFireFunctions,
        private firebasedb: FirebaseService,
        private toast: HotToastService,) {

        firebase
        .list('/main')
        .valueChanges()
        .subscribe((users) => {
          this.mainGrid.dataSource = users; //intial data binding to grid
         
        });

      firebase
      .list('/main')
      .snapshotChanges()
      .subscribe((users) => {
        this.mainGrid.dataSource = users; // sync server data changes to grid
      });

         
        }


        @ViewChild('mainGrid')
        public mainGrid: GridComponent;
        public editSettings: EditSettingsModel;
        public toolbar: ToolbarItems[];
        public filterOption: FilterSettingsModel = { type: 'Excel' };
        sortOptions: { columns: { field: string; direction: string }[] };
     
     

      public actionCompleteMain(args: any): void {
        switch (args.requestType) {
          case 'save':
                 this.firebasedb.updateMain(JSON.parse(JSON.stringify(args.data)));
            break;
          case 'delete':
            args.data.forEach((row: any) => {
              this.firebasedb.removeMain(row.key);
            });
         
            break;
        }
      }


      detailDataBound(e: DetailDataBoundEventArgs) {
        var index=parseInt((e as any).detailElement.children[0].parentElement.parentElement.previousSibling.getAttribute('data-rowindex'))
      let detail = new Grid({
        dataSource:  this.mainGrid.dataSource[index]['children'],
        toolbar: ['Add'],
        editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true,  mode: 'Dialog', },
        columns: [
          {field:'orderID', headerText:'orderID', width:'70' ,textAlign:'Right'},
          { field: 'TotalCosts', headerText: 'TotalCosts', width: 140 },
          { field: 'UnitWeight', headerText: 'UnitWeight', width: 150 },
        ],
      });
      detail.appendTo((e as any).detailElement.querySelector('.custom-grid'));
    }



    JB Jonas Blazinskas April 12, 2023 09:03 PM UTC

    So I have managed to bind to the data like this but now I am running into issues where I can not trigger function with event args of the child grid





    export class MainComponent {
    constructor(
    public firebase: AngularFireDatabase,
    private fns: AngularFireFunctions,
    private firebasedb: FirebaseService,
    private toast: HotToastService
    ) {
    firebase
    .list('/main')
    .valueChanges()
    .subscribe((users) => {
    this.mainGrid.dataSource = users; //intial data binding to grid
    });

    firebase
    .list('/main')
    .snapshotChanges()
    .subscribe((users) => {
    this.mainGrid.dataSource = users; // sync server data changes to grid
    });
    }

    @ViewChild('mainGrid')
    public mainGrid: GridComponent;
    public editSettings: EditSettingsModel;
    public toolbar: ToolbarItems[];
    public filterOption: FilterSettingsModel = { type: 'Excel' };
    sortOptions: { columns: { field: string; direction: string }[] };
    public commandsInvoice: CommandModel[];
    public childGrid: GridModel = {
    toolbar: ['Add', 'Delete'],
    queryString: 'key',
    editSettings: {
    allowEditing: true,
    allowAdding: true,
    allowDeleting: true,
    mode: 'Dialog',
    },
    columns: [
    { field: 'oil', headerText: 'Oil', width: 50 },
    ],
    actionBegin(args: any) {
    let key = this.parentDetails.parentKeyFieldValue;

    switch (args.requestType) {
    case 'save':
    console.log(key)
    console.log(args.data)
    this.firebasedb.updateJobs();
    break;
    case 'delete':
    console.log(key)
    console.log(args.data)
    this.firebasedb.updateJobs();
    break;
    }
    },
    };


    public actionCompleteMain(args: any): void {
    switch (args.requestType) {
    case 'save':
    this.firebasedb.updateMain(JSON.parse(JSON.stringify(args.data)));
    break;
    case 'delete':
    args.data.forEach((row: any) => {
    this.firebasedb.removeMain(row.key);
    });

    break;
    }
    }


    detailDataBound(e: any) {

    let key = e.data.key;

    this.firebase
    .list(`/main/${key}/children`)
    .snapshotChanges()
    .subscribe((users) => {
    e.childGrid.query = new Query();
    e.childGrid.dataSource = users; // sync server data changes to grid
    });

    this.firebase
    .list(`/main/${key}/children`)
    .valueChanges()
    .subscribe((users) => {
    e.childGrid.query = new Query();
    e.childGrid.dataSource = users; //intial data binding to grid
    console.log(users);
    });
    }




    //From firebasedb :
    //main
    removeMain(key: string) {
    this.db.object(`${this.mainUrl}/${key}`).remove();
    }

    updateMain(value) {
    if (!value.key) {
    value.key = this.db.createPushId();
    }
    this.db.database.ref(this.mainUrl).child(value.key).update(value);
    }

    //job
    removeJob(key: string, parentkey) {
    this.db.object(`${this.mainUrl}/${parentkey}/child/${key}`).remove();
    }

    updateJobs() {
    console.log("test")
    // console.log(value)
    // console.log(parentkey)
    // if (!value.key) {
    // value.key = this.db.createPushId();
    // }
    // this.db.database.ref(this.mainUrl).child(value.key).update(value);
    }




    So actionCompleteMain works fine with the main grid but, child grid, actionBegin( cosole logs work fine but the triggering of firebasedb functions returns this error:




    PS Pavithra Subramaniyam Syncfusion Team April 18, 2023 11:47 AM UTC

    Frank Alberts,


    From the error, we understand that the issue occurs due to the “this” reference inside the child “actionBegin” event which denotes the Child Grid reference, not the angular component. So, trying to access the angular component property throws the error. So, we suggest the arrow function to have the component reference scope for the child Grid actionBegin event and get the key value from the argument passed to the event.



    JB Jonas Blazinskas April 18, 2023 01:24 PM UTC

    Hi Pavithra,

    I am trying to figure out your statement, but while I do that would it be possible to give a small code snippet for me to reference the ideas as I am not that much of an advanced developer and I can go wrong understanding the terminology,

    Thank you for your help so much!



    JB Jonas Blazinskas April 21, 2023 09:21 PM UTC

    Hey quick update, so I have figured out how to do the arrow function. But it broke prentKeyFieldValue

          let key = this.parentDetails.parentKeyFieldValue;


    I have a workaround by just setting a property myself from detail bound, but is there a more elegant way to do this like previously?


    export class MainComponent {

      parentKey: any;
     
      constructor(
        public firebase: AngularFireDatabase,
        private fns: AngularFireFunctions,
        private firebasedb: FirebaseService,
        private toast: HotToastService
      ) {
        firebase
          .list('/main')
          .valueChanges()
          .subscribe((users) => {
            this.mainGrid.dataSource = users; //intial data binding to grid
          });

        firebase
          .list('/main')
          .snapshotChanges()
          .subscribe((users) => {
            this.mainGrid.dataSource = users; // sync server data changes to grid
          });
      }



      @ViewChild('mainGrid')
      public mainGrid: GridComponent;
      public editSettings: EditSettingsModel;
      public toolbar: ToolbarItems[];
      public filterOption: FilterSettingsModel = { type: 'Excel' };
      sortOptions: { columns: { field: string; direction: string }[] };
      public commandsInvoice: CommandModel[];



      public dropdownRates: object[] = [
        { value: '1', text: 'Hourly' },
        { value: '0', text: 'Flat' },
      ];
      public dropdown2: object[] = [
        { uid: 'Waiting', countryId: '1' },
        { uid: 'In Progress', countryId: '2' },
        { uid: 'Finished', countryId: '3' },
      ];
      public dropdownParams2 = {
        params: {
          allowFiltering: true,
          dataSource: new DataManager(this.dropdown2),
          fields: { text: 'uid', value: 'uid' },
          query: new Query(),
          actionComplete: () => false,
        },
      };
      public userparams = {
        params: {
          allowFiltering: true,
          fields: { text: 'email', value: 'email' },
          query: new Query(),
          actionComplete: () => false,
        },
      };




      public childGrid: GridModel = {
        toolbar: ['Add', 'Delete'],
        queryString: 'key',
        editSettings: {
          allowEditing: true,
          allowAdding: true,
          allowDeleting: true,
          mode: 'Dialog',
        },
        columns: [
          {
            field: 'jobTitle',
            headerText: 'Job Title',
            textAlign: 'Right',
            allowEditing: true,
            width: 120,
          },
          { field: 'worker', headerText: 'Worker Name', width: 150 , editType: 'dropdownedit', edit:this.userparams},
          { field: 'rateType', headerText: 'Rate Type', width: 50, allowEditing: true, foreignKeyValue:'text' ,foreignKeyField:'value', dataSource: this.dropdownRates  },
          { field: 'rateQty', headerText: 'Rate Qty', width: 50, allowEditing: true, },
          { field: 'ebay', headerText: 'Ebay', width: 50, allowEditing: true, },
          { field: 'euroCarParts', headerText: 'EuroCarParts', width: 50, allowEditing: true, },
          { field: 'sdl', headerText: 'SDL', width: 50, allowEditing: true, },
          { field: 'imex', headerText: 'IMEX', width: 50, allowEditing: true, },
          { field: 'dawmac', headerText: 'Dawmac', width: 50, allowEditing: true, },
          { field: 'salvageYard', headerText: 'SalvageYard', width: 50, allowEditing: true, },
          { field: 'oil', headerText: 'Oil', width: 50 },
          {
            field: 'priceMulti',
            headerText: 'Price Multi',
            width: 50,
            allowEditing: true,
          },
          { field: 'time', headerText: 'Spent Time', width: 50 },
          { field: 'date', headerText: 'Date', width: 150 , type: 'date', format: 'dd/MM/yyyy', editType: 'datepickeredit'},
          { field: 'cof', headerText: 'Cof', width: 50, allowEditing: true },
       
          {
            field: 'price',
            headerText: 'Final Price',
            width: 50,
            allowEditing: false,
          },
          {
            field: 'salary',
            headerText: 'Worker Salary',
            width: 50,
            allowEditing: false,
          },
          { field: 'profit', headerText: 'Profit', width: 50, allowEditing: false },
        ],

     
     
        actionComplete: (args: any) => {
       console.log(args)
          switch (args.requestType) {
            case 'save':

              console.log(args);
              console.log(this.parentKey);
              this.firebasedb.updateJobs("Test");
              break;
            case 'delete':

              console.log(args.data);
              this.firebasedb.updateJobs("Test");
              break;
          }
        },
      };



      public actionCompleteMain(args: any): void {
        switch (args.requestType) {
          case 'save':
            console.log(args);
            this.firebasedb.updateMain(JSON.parse(JSON.stringify(args.data)));
            break;
          case 'delete':
            args.data.forEach((row: any) => {
              this.firebasedb.removeMain(row.key);
            });

            break;
        }
      }


      detailDataBound(e: any) {

        this.parentKey = e.data.key;

        this.firebase
          .list(`/main/${this.parentKey}/children`)
          .snapshotChanges()
          .subscribe((users) => {
            e.childGrid.query = new Query();
            e.childGrid.dataSource = users; // sync server data changes to grid
          });

        this.firebase
          .list(`/main/${this.parentKey}/children`)
          .valueChanges()
          .subscribe((users) => {
            e.childGrid.query = new Query();
            e.childGrid.dataSource = users; //intial data binding to grid
          });
      }




    VS Vikram Sundararajan Syncfusion Team April 27, 2023 10:08 AM UTC

    Hi Frank,


    In actionBegin event, you can get the parentKeyFieldValue using parentsUntil method. The parentsUntil method is likely a custom function that searches for the nearest ancestor element of the given element with the specified class name ('e-grid') and returns it. If the requestType is 'save', the function retrieves a reference to a Grid control using the parentsUntil method. If the requestType is 'delete', the function retrieves a reference to the Grid control using the parentsUntil method with the first element of the args.tr array as the argument. This is likely the row that is being deleted.


    Please refer the below code example for more information.


     [app.component.ts]

     

     actionBegin(argsany) {

            switch (args.requestType) {

              case 'save':

                console.log(args);

                var grid = (parentsUntil(args.form'e-grid'as any).ej2_instances[0];

                console.log(grid.parentDetails);

                break;

              case 'delete':

                var grid = (parentsUntil(args.tr[0], 'e-grid'as any).ej2_instances[0];

                console.log(grid.parentDetails);

                break;

            }

          },



    Sample: https://stackblitz.com/edit/angular-mddxjg-6bgkie?file=src%2Fapp.component.ts


    If you require further assistance, please do not hesitate to contact us. We are always here to help you.


    Loader.
    Up arrow icon