Foreing Key Value - Datasource from Firebase

Hi I am struggling a bit to combine multiple concepts into one here.

  1. I am trying have a column which is a dropdown and is called "parent". (done)
  2. it contains the datasource which is the same as the grid datasource for the dropdown values. (done)

  3.  I am trying to show "companyName" property both in the column dropdown and the column itself in the UI, but I would like to store "key" property. I know this is done with foreignKeyValue and foreignKeyField. But my attempts have failed me so far. 


 <ejs-grid
    [locale]='"de-DE"'
    class="location-grid"
    #locationGrid
    (actionComplete)="actionComplete($event)"
    (actionBegin)="actionBegin($event)"
    (actionFailure)="actionFailure($event)"
    (rowSelected)="onLocRowSelected($event)"
    autoFit="true"
    allowResizing="true"
    [editSettings]="editSettings"
    [toolbar]="locToolbar"
    showToolbar="true"
    allowFiltering="true"
    [filterSettings]="filterLocations"
    height="calc(50vh - 30px - 87px - 1px)"
    [allowSorting]="true"
    [selectedRowIndex]="selectedRowLoc"
    (toolbarClick)='toolbarClickHandler($event)'

 
  >
    <e-columns>
      <e-column field="companyName" headerText="Firma"></e-column>


      <e-column field="parent" headerText="Parent"

      editType="dropdownedit"
      [edit]="editparams"></e-column>


    </e-columns>
  </ejs-grid>
</div>


ngOnInit

 this.editparams = {
      params: {
        allowFiltering: true,
        // dataSource: new DataManager(this.users),
        fields: { text: 'companyName', value: 'key' },
        query: new Query(),
        actionComplete: () => false,
      },
    };

    this.editSettings = {
      allowEditing: true,
      allowAdding: true,
      allowDeleting: true,
      mode: 'Dialog',
    };


Constructor

  firebase
      .list('/locations')
      .valueChanges()
      .subscribe((locations: any) => {
       
        this.locations = locations;
        this.locationGrid.dataSource = locations; //intial data binding to grid


        (
          (this.locationGrid.columns[13] as Column).edit.params as DropDownListModel
        ).dataSource = new DataManager(this.locations);
       

 
      });












10 Replies

SI Santhosh Iruthayaraj Syncfusion Team November 10, 2023 01:44 PM UTC

Hi Frank Alberts,


We apologize for the delayed response.


From your query, we understand that you seek clarification on how to use the Foreign Key Column feature. Based on this, we have explained the concept with a sample, which you can find below:


We have prepared a grid with a dataSource named "orderDetails" containing order-related data. This dataSource will only contain the "Key" field under the name "CustomerID" which related to the Customer. We have bound the Foreign Key Column dataSource, namely "customerData", which will consist of both the "Key" field and the "CompanyName" field, along with other customer-related data. The "CustomerID" field from the Grid dataSource and the "Key" field from the Foreign Key dataSource will act as a common field. To retrieve the "CompanyName" from the foreign key dataSource using the "CustomerID" present in the Grid dataSource, we need to bind the "column.field" as "CustomerID" because the field is where the common field is present for Grid dataSource. Now, the "column.foreignKeyField" property needs to be bound with the field name from the Foreign Key dataSource, which will act as a Foreign Key - in our case, it is "Key". Next, we need to bind the value that needs to be displayed in the Grid column from the Foreign Key dataSource "column.foreignKeyValue" property, which is the "CompanyName". Now that everything is set, the Grid will render with the “CompanyName“ from the Foreign Key dataSource. You don’t need to modify anything related to the drop-down edit; when binding the Foreign Key dataSource, the drop-down editor in the Grid will, by default, display the Foreign Key Value (CompanyName) in the drop-down popup and store the Foreign Key (Key) in the Grid dataSource. You can find a sample implementing this explanation from the link below:


Sample: https://stackblitz.com/edit/angular-grid-foreign-key-columnn


You can find more information about the Foreign Key Column in the documentation link below:


Documentation: https://ej2.syncfusion.com/angular/documentation/grid/columns/foreign-key-column


We hope this information clarifies your concerns about the Foreign Key Column feature. Please let us know if you have any further queries or require additional assistance.


Regards,

Santhosh I



JB Jonas Blazinskas November 12, 2023 06:18 PM UTC

Hi Santhosh,

I think it's not that simple in my case as I get the following error:

core.mjs:10592 ERROR TypeError: Cannot set properties of undefined (setting 'dataSource')

    at Object.next (admin.component.ts:210:21)

    at ConsumerObserver.next (Subscriber.js:91:33)

    at SafeSubscriber._next (Subscriber.js:60:26)

    at SafeSubscriber.next (Subscriber.js:31:18)

    at observeOn.js:6:130

    at AsyncAction.<anonymous> (executeSchedule.js:3:9)

    at angular-fire.js:104:22

    at _ZoneDelegate.invoke (zone.js:368:26)

    at Object.onInvoke (core.mjs:11061:33)

    at _ZoneDelegate.invoke (zone.js:367:52)



I am thinking maybe because the data arrives too late? Or is because it's classing with the data that I set for the dropdown?

<ejs-grid
    [locale]='"de-DE"'
    class="location-grid"
    #locationGrid
    (actionComplete)="actionComplete($event)"
    (actionBegin)="actionBegin($event)"
    (actionFailure)="actionFailure($event)"
    (rowSelected)="onLocRowSelected($event)"
    autoFit="true"
    allowResizing="true"
    [editSettings]="editSettings"
    [toolbar]="locToolbar"
    showToolbar="true"
    allowFiltering="true"
    [filterSettings]="filterLocations"
    height="calc(50vh - 30px - 87px - 1px)"
    [allowSorting]="true"
    [selectedRowIndex]="selectedRowLoc"
    (toolbarClick)='toolbarClickHandler($event)'

 
  >
    <e-columns>
      <e-column field="companyName" headerText="Firma"></e-column>


      <e-column field="parent" headerText="Parent"
      foreignKeyField="key"
      foreignKeyValue="companyName"
      [dataSource]="locations"
      editType="dropdownedit"
      [edit]="editparams"></e-column>


    </e-columns>
  </ejs-grid>
</div>




this.editparams = {
  params: {
    allowFiltering: true,
    // dataSource: new DataManager(this.users),
    fields: { text: 'companyName', value: 'key' },
    query: new Query(),
    actionComplete: () => false,
  },
};

this.editSettings = {
  allowEditing: true,
  allowAdding: true,
  allowDeleting: true,
  mode: 'Dialog',
};




firebase
.list('/locations')
.valueChanges()
.subscribe((locations: any) => {
 
  this.locations = locations;
  this.locationGrid.dataSource = locations; //intial data binding to grid


  (
    (this.locationGrid.columns[13] as Column).edit.params as DropDownListModel
  ).dataSource = new DataManager(this.locations);
 


});


JB Jonas Blazinskas November 12, 2023 06:37 PM UTC

Somehow the two ideas here are not jelling together maybe I should just define the requirements and see if you could create a demo that ticks all the boxes:

1) Dialog edit for that column has a dropdown showing the values of "comapnyName" but actually storing the "key".

2) The datasource for this column is set only when the data arrives and has been achieved with another ticket before like this:

   <e-column field="parent" headerText="Parent"
      editType="dropdownedit"
      [edit]="editparams"></e-column>


Setting the datasource after data arrives:

    this.editparams = {
      params: {
        allowFiltering: true,
        fields: { text: 'companyName', value: 'key' },
        query: new Query(),
        actionComplete: () => false,
      },
    };

 firebase
      .list('/locations')
      .valueChanges()
      .subscribe((locations: any) => {
       
        (
          (this.locationGrid.columns[13] as Column).edit.params as DropDownListModel
        ).dataSource = new DataManager(this.locations);
       

 
      });



3) Now that you can select a "companyName" form a dropdown and store its "key". This can be seen in the grid as a "key".  I would like to convert it to the "companyName" as well on the front end.





SI Santhosh Iruthayaraj Syncfusion Team November 14, 2023 01:17 PM UTC

Hi Frank Alberts,


We have some concerns regarding your explanation and the provided code snippet. Before proceeding further, could you please review the explanations below and confirm your expectations based on them? This will enable us to move forward more effectively.


  1. Firstly, from your code, it is evident that you are binding the same dataSource, "locations," for the Grid, Column, and DropDownList, all sourced from Firebase. However, the use of a Foreign Key column is intended to display values from another table data in the Grid column. This implies that the Grid dataSource and the Foreign Key Column dataSource should be different. In our provided example, the Grid dataSource is named "orderDetails", containing Order Details. Storing customer details like Customer Name, Customer Address, and Company Name in the "orderDetails" dataSource for each order is not efficient, given that multiple orders may have the same customer. The solution involves using a Foreign Key column, where only the "key" related to the customer is stored in the "orderDetails" data. All other customer details are stored in a secondary dataSource, "customerDetails", bound to the Foreign Key column. This ensures efficiency by avoiding repetitive customer data. Both "orderDetails" and "customerDetails" share the "key" property, acting as the Primary Key in "customerDetails" (Foreign Key Column dataSource) and the Foreign Key in "orderDetails" (Grid dataSource). Since the same “key” field is acting as Primary Key in Foreign Key column dataSource and Foreign Key in the Grid dataSource, binding the same dataSource for both the Grid and Foreign Key column is not appropriate and does not adhere to the intended use of this feature.
  2. Regarding the use of the Foreign Key Column feature drop down binding, the column will automatically render the "dropdownedit" during editing and display the "column.foreignKeyValue" (“companyName”) in the dropdown popup and store the “column.foreignKeyField” (“key”) in the Grid dataSource. Therefore, manually binding the fields using the edit params, such as "{ text: 'companyName', value: 'key' }," is unnecessary.


We have already provided a sample that clearly implements these points. Additional information about the Foreign Key Column feature can be found in our documentation. We strongly recommend implementing the Foreign Key Column based on the provided explanation and the documentation to achieve a proper output.


Sample: https://stackblitz.com/edit/angular-grid-foreign-key-columnn


Foreign Key Column: https://ej2.syncfusion.com/angular/documentation/grid/columns/foreign-key-column


Regards,

Santhosh I



JB Jonas Blazinskas November 14, 2023 01:57 PM UTC

  1. That is correct I am intentionally using same datasource there, because all I am trying to do is store one property from that dataSource yet display another property from the same dataSouce.

    If this somehow causes a bug is there a way to trick this and create a copy of datasource and then bind it? Or is there another way all together instead of foreignKey?



    My attempt replicating your example:

    So I tried to just fetch the data again and feed it as per your example and I think there must be something else going on


firebase
.list('/locations')
.valueChanges()
.subscribe((locations: any) => {

this.locationGrid.dataSource = locations; //intial data binding to grid



});


firebase
.list('/locations')
.valueChanges()
.subscribe((locations: any) => {
this.differentLocations = locations
});



asdasd

<ejs-grid
[locale]="'de-DE'"
class="location-grid"
#locationGrid
(actionComplete)="actionComplete($event)"
(actionBegin)="actionBegin($event)"
(actionFailure)="actionFailure($event)"
(rowSelected)="onLocRowSelected($event)"
autoFit="true"
allowResizing="true"
[editSettings]="editSettings"
[toolbar]="locToolbar"
showToolbar="true"
allowFiltering="true"
[filterSettings]="filterLocations"
height="calc(50vh - 30px - 87px - 1px)"
[allowSorting]="true"
[selectedRowIndex]="selectedRowLoc"
(toolbarClick)="toolbarClickHandler($event)"
>
<e-columns>
<e-column field="companyName" headerText="Firma">e-column>
<e-column field="firstName" headerText="Vorname">e-column>
<e-column field="lastName" headerText="Name">e-column>
<e-column field="address" headerText="Adresse">e-column>
<e-column field="postcode" headerText="PLZ#">e-column>
<e-column field="city" headerText="Ort">e-column>
<e-column field="phone" headerText="Tel" [visible]="false">e-column>
<e-column field="email" headerText="Email" [visible]="false">e-column>
<e-column field="comment" headerText="Kommentar">e-column>
<e-column field="VAT" headerText="MwSt Nr." [visible]="false">e-column>
<e-column field="VeVa" headerText="VeVa Nr." [visible]="false">e-column>
<e-column field="type" headerText="Type">e-column>

<e-column
field="parent"
headerText="Parent"
foreignKeyField="key"
foreignKeyValue="companyName"
[dataSource]="differentLocations"
>e-column>
<e-column
textAlign="center"
field="archived"
headerText="Archived"
editType="booleanedit"
type="boolean"
[displayAsCheckBox]="true"
[visible]="false"
>e-column>
e-columns>
ejs-grid>



I think this error might be related

Image_2832_1699969636598



as I have replicated your steps I feel like my point about this being data that arrives somewhat later might be causing a bug actually? But I let you be the judge


SI Santhosh Iruthayaraj Syncfusion Team November 22, 2023 01:12 PM UTC

Hi Frank Alberts,


From your query, we understand that you want to display another column value in a column and also show other column values in a drop-down while editing. However, you need to save the current field value in the dataSource during editing.


To display another column value in a column, you can utilize the Column Template feature of the Grid. For Drop Down Edit, you can render a custom DropDownList component as an editor for that particular column using the Custom Editor feature. This allows you to customize the text, value, and dataSource, updating the value of the Editor based on your requirements. Below is a code snippet and sample:


[app.component.html]

 

  <e-column

      field="key"

      headerText="Company Name"

      width="150"

      [edit]="ddParams"

    >

      <!-- column template to display other column value -->

      <ng-template #template let-data>

        {{ data.CompanyName }}

      </ng-template>

    </e-column>

 

[app.component.ts]

 

// custom DropDownList editor

 ngOnInit(): void {

   .  .  .  .  .

    this.ddParams = {

      create: this.createDDFn,

      read: this.readDDFn,

      destroy: this.destroyDDFn,

      write: this.writeDDFn,

    };

  }

 

  public createDDFn() {

    this.ddElem = document.createElement('input');

    return this.ddElem;

  }

 

  public destroyDDFn() {

    this.ddObj.destroy();

  }

 

  public readDDFn() {

    return this.ddObj.value;

  }

 

  public writeDDFn(argsany) {

    this.ddObj = new DropDownList({

      value: args.rowData[args.column.field],

      dataSource: [...customerDetails],

      fields: { text: 'CompanyName'value: 'Key' },

    });

    this.ddObj.appendTo(this.ddElem);

  }

 


Sample: https://stackblitz.com/edit/angular-grid-with-custom-editor


You can find the documentation references for this implementations from the below links:


Although the provided solution appears to work as expected, it might encounter issues because your requirement is not entirely valid. While displaying other column values in a column using the Column Template feature works properly, the editing scenario faces challenges. The current implementation display “CompanyName” value and stores “Key” value, requiring you to edit both the displayed value and the specific column value. But your requirement is to edit only the “Key” field and if we do that the Grid will display the same “CompanyName” value after editing because we only edited the “Key” field value.


Additionally for performing CRUD operation in a dataSource, you need to set one column as a Primary Key to enable CRUD operations like edit, add, and delete. This is not only for the Grid component by general when comes to editing there should be one Primary Key which will help get the unique record to perform CRUD operation. However, the provided code lacks the specification of a Primary Key, and the Grid won't perform CRUD operations correctly without it. The Primary Key column should have unique and uneditable values. The absence of this setting results in conflicting requirements, as you want to edit the "Key" field value, which cannot be set as a Primary Key.


Please refer to the below documentation for more information about editing and Primary Key column:

https://ej2.syncfusion.com/angular/documentation/grid/editing/edit


When it comes to the DropDownList dataSource, both the "text" and "value" should be unique. This is because, by default, the DropDown values must be unique. However, in the Grid dataSource, multiple rows can have the same "Key" and "CompanyName" since the "Key" field in the Grid is not a Primary Key. Consequently, it will contain duplicate values when editing and saving multiple rows with the same value.


All these cases will contradict your requirements. Therefore, using the same dataSource will not work as expected. While it may seem implementable, in practice, it is not feasible. You either need to have a different dataSource for the Grid and DropDown and perform editing operations on both the Key and CompanyName columns, or you need to display the column that should be edited (Key).


The only possible and recommended approach for your requirement is create a separate dataSource for your DropDownList containing all the unique possible combinations of "Key" and "CompanyName" values. Assign this dataSource to the DropDownList and use it as a Foreign Key dataSource for the Foreign Key Column, as explained in our initial response. This is the only proper way to achieve your requirement; otherwise, you will consistently encounter multiple issues.


We hope this clarification addresses your concerns. Feel free to reach out if you have further queries.


Regards,

Santhosh I



JB Jonas Blazinskas November 24, 2023 08:16 PM UTC

Hi Santhosh,

At this point, I am even more confused.

Let's simplify the problem and go back to the start please if you can.

https://www.syncfusion.com/forums/178743/dropdown-edit-observable-list

^So a year ago I wanted to have a dropdown with a list of emails of my users that I fetch from firebase. And that was how I was told to implement a year ago.

So if you could show how could I simply do similar here as I suspect some changes might have happened.



Challenge:
Set dialog edit column  <e-column field="type" headerText="Type"     ></e-column> to have dropdown with values from  this.dataColors   specifically I want values of  this.dataColors.propertyName 

constructor:

  firebase
      .list('/colors')
      .valueChanges()
      .subscribe((data: any) => {
        this.dataColors = data;
   
      });



  <ejs-grid
    [locale]="'de-DE'"
    class="location-grid"
    #locationGrid
    (actionComplete)="actionComplete($event)"
    (actionBegin)="actionBegin($event)"
    (actionFailure)="actionFailure($event)"
    (rowSelected)="onLocRowSelected($event)"
    autoFit="true"
    allowResizing="true"
    [editSettings]="editSettings"
    [toolbar]="locToolbar"
    showToolbar="true"
    allowFiltering="true"
    [filterSettings]="filterLocations"
    height="calc(50vh - 30px - 87px - 1px)"
    [allowSorting]="true"
    [selectedRowIndex]="selectedRowLoc"
    (toolbarClick)="toolbarClickHandler($event)"
  >
    <e-columns>
      <e-column field="companyName" headerText="Firma"></e-column>
      <e-column field="firstName" headerText="Vorname"></e-column>
      <e-column field="lastName" headerText="Name"></e-column>
      <e-column field="address" headerText="Adresse"></e-column>
      <e-column field="postcode" headerText="PLZ#"></e-column>
      <e-column field="city" headerText="Ort"></e-column>
      <e-column field="phone" headerText="Tel" [visible]="false"></e-column>
      <e-column field="email" headerText="Email" [visible]="false"></e-column>
      <e-column field="comment" headerText="Kommentar"></e-column>
      <e-column field="VAT" headerText="MwSt Nr." [visible]="false"></e-column>
      <e-column field="VeVa" headerText="VeVa Nr." [visible]="false"></e-column>
      <e-column field="type" headerText="Type"     ></e-column>


    </e-columns>
  </ejs-grid>




Can you show me how to achieve this first? And then we could focus again on the harder scenario. Because it seems I cant even achieve this anymore with the old metho



SI Santhosh Iruthayaraj Syncfusion Team November 30, 2023 01:46 PM UTC

Hi Frank Alberts,


Following your recent query, it appears that you intended to bind the data fetched from Firebase to the DropDownList dataSource. We have successfully implemented this requirement by utilizing the actionComplete event of the Grid. Please find the corrected code snippet below:


[app.component.ts]

 

  constructor(public serviceOrdersService) {

    // taking first five records from API

    this.service.getData({ take: 5 }).subscribe((valueany=> {

      // bind it to the dataColors property

      this.dataColors = value.result;

    });

  }

 

  actionComplete(args) {

    if (args.requestType === 'beginEdit') {

      // use your field name inside querySelector

      let ddObj = args.form.querySelector('input[name="ShipCountry"]')

        .ej2_instances[0];

      // assign the fetched value from the dataColors to the DropDownList

      ddObj.dataSource.dataSource.json = this.dataColors;

    }

  }

 


Additionally, if you wish to configure the DropDownList "fields" property, you can achieve this by using the Edit Params. Please find the corrected code snippet below:


[app.component.ts]

 

 public ngOnInit(): void {

   .  .  .  .  .

    this.editParams = {

      params: {

        fields: { text: ’ShipCountry’value: 'ShipCountry' },

        query: new Query(),

      },

    };

  }

 


Sample: https://stackblitz.com/edit/angular-grid-observables-binding


Regards,

Santhosh I



JB Jonas Blazinskas December 6, 2023 01:59 PM UTC

Hi Santhosh,

Thank you so that one worked well.

So now I have two dropdowns columns like this:


  <e-column
        field="type"
        headerText="Type"
        editType="dropdownedit"
        [edit]="editparamsType"
      ></e-column>

      <e-column
        field="parent"
        headerText="Parent"
        editType="dropdownedit"
        [edit]="editparams"
      ></e-column>


 this.editparams = {
      params: {
        allowFiltering: true,
        fields: { text: 'companyName', value: 'key' },
        query: new Query(),
        actionComplete: () => false,
      },
    };
    this.editparamsType = {
      params: {
        allowFiltering: true,
        fields: { text: 'propertyName', value: 'propertyName' },
        query: new Query(),
        actionComplete: () => false,
      },
    };


public actionComplete(args: any): void {
    // assign drowdown data
    if (args.requestType === 'beginEdit') {

      // use your field name inside querySelector
      let ddObj = args.form.querySelector('input[name="type"]')
        .ej2_instances[0];

      // assign the fetched value from the dataColors to the DropDownList
      ddObj.dataSource.dataSource.json = this.dataColors;
   
    } }



It work good now, now I would just like to display companyText instead of key in the Parent column.

As you see in the dialog edit I get the accurate companyText and the key gets stored as data. Which is exactly what I want here.

Image_8306_1701870710486


But the grid displays key which is not what I would like in the perfect world I want it to act exactly like the dialog and display companyText but in the background store the key.

Image_6036_1701870818967


Can you show me how I would be able to accomplish this successfully?

At the moment this works without setting the column datasource as its infact same datasource as the grid itself.


I am trying to set parent child relationships within the grid itself by the use of this Parent column. 
















SI Santhosh Iruthayaraj Syncfusion Team December 8, 2023 10:48 AM UTC

Hi Frank Alberts,


Following your recent response, it appears that you have successfully implemented the DropDownList part. Your current requirement is to display the "companyText" in place of the "key" column values. According to your implementation and the information provided, both "companyText" and "key" field values should exist in the same Grid dataSource. In this scenario, you can display the "companyText" values in the "key" column using the Column Template feature of the Grid component. Please refer to the code snippet and sample provided below:


[app.component.html]

 

    <e-column field="key" headerText="Company Name" width="150">

      <!-- column template to display "CompanyName" values in "key" column -->

      <ng-template #template let-data>

        {{ data.CompanyName }}

      </ng-template>

    </e-column>

 


Sample: https://stackblitz.com/edit/angular-grid-with-column-template


As evident in the above code snippet, the Column Template can receive the record data in the data variable. Using the data variable, you can display any other field value in the current column.


For more information about the Column Template feature, please visit the following documentation link:


Column Template: https://ej2.syncfusion.com/angular/documentation/grid/columns/column-template


Regards,

Santhosh I


Loader.
Up arrow icon