This is a migrated thread and some comments may be shown as answers.

Are custom combobox values possible in grid?

11 Answers 802 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Ron
Top achievements
Rank 1
Iron
Iron
Iron
Ron asked on 01 Mar 2018, 04:05 AM

I've been trying at this for a couple days and noticing the same thing, ie. that I can get a combobox to read back selected values and record them successfully to a grid, I can get a combobox on its own to take and save the new custom values to my back-end through REST, but I can't combine the two.

As far as the API's are concerned this combination works for in-grid selection:

          <kendo-combobox [formControl]="dimensionTypeGridFormGroup.get('dimensionTypeId')"
                          [data]="dimensiontypes"
                          [textField]="'name'"
                          [valueField]="'id'"
                          [valuePrimitive]="true">
          </kendo-combobox>

This for creating new content:

<kendo-combobox [allowCustom]="true"
                [data]="combolists"
                [textField]="'name'"
                [valueField]="'id'"
                [valueNormalizer]="valueNormalizer"
                [(ngModel)]="clbuilder">
</kendo-combobox>

It seems like the moment I add [allowCustom] to a combobox in a grid cell it causes two things to happen:

1) the grid cell will be blank on save

2) only the default value is saved in the grid formgroup for that cell.

In a lot of cases I could indeed save my new record to my back end but the grid would stop working. In some of my trial and error I noticed that confusion was occurring (in the combobox API references) between the form control data and the combobox data (such as 'id' in the formGroup vs. in the export interface array). 

 

Is there a right way to do this through the combobox API components and, if not, is there a way that I could push the values I need back to the grid from somewhere else like a valueChange or valueNormalizer method? I remember there being an edit.service push back to grid in the in-cell grid editing example but I'm not sure how to hit that from within a component yet.

11 Answers, 1 is accepted

Sort by
0
Dimiter Topalov
Telerik team
answered on 02 Mar 2018, 12:34 PM
Hello Ron,

To achieve the desired functionality, you will need the valueNormalizer method as mentioned to create the new item, based on the provided text. When the component is bound to a primitive value (valuePrimitive = true), this method should return the primitive value:

public valueNormalizer = (text) => text.pipe(map((text: string) => {
      const newItem = {
          CategoryID: this.categories[this.categories.length - 1].CategoryID + 1,
          CategoryName: text
      };
       
      return newItem.CategoryID;
    }));

Depending on how the data the ComboBox is bound to is retrieved, you will also need to ensure that the newly created value is added to this list too so that it can be then displayed in the Grid.

Here is a sample implementation of the described approach:

https://plnkr.co/edit/G7vYGmQXfJ7y8vNacURn?p=preview

I hope this helps.

Regards,
Dimiter Topalov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 02 Mar 2018, 03:23 PM

Dimiter,

I'll clarify something I'm running into.

The grid is set up as a Reactive form. The interface for the value in the combobox looks like this:

    export interface IDimensionType {
      id: string;
      name: string;
    }

The id field needs to intersect with dimensionTypeId in the grid line:

    export interface IFastenerTypeDimension {
      id: string;
      fastenerTypeId: string;
      dimensionTypeId: string;
      fastenerOptionId: string;
    }

I'm able to save a new dimensionType  without much trouble from the example above but the trouble has been including the updated value in the grid (which shows up blank) and saving that new value.

My current valueNormalizer with a demo guid looks like this:

  public valueNormalizer = (text: Observable<string>) => text.pipe(map((text: string) => {

    let idimensionType: IDimensionType = {
      'id': 'a0000000-0000-0000-0000-000000000009',
      'name': text
    }

    this.dimensionTypeService.save(idimensionType, true);

    return {
      id: 'a0000000-0000-0000-0000-000000000009',
      name: text
    };
  }));

I need that value, a0000000-0000-0000-0000-000000000009, pushed back to the grid as well as either the editHandler:

    public editHandler({ sender, rowIndex, dataItem, isNew }) {
      this.closeEditor(sender);

      this.dimensionTypeGridFormGroup = new FormGroup({
        'id': new FormControl(dataItem.id),
        'fastenerTypeId': new FormControl(dataItem.fastenerTypeId),
        'dimensionTypeId': new FormControl(dataItem.dimensionTypeId),
        'fastenerOptionId': new FormControl(dataItem.fastenerOptionId)
      });
      this.editedRowIndex = rowIndex;
      sender.editRow(rowIndex, this.dimensionTypeGridFormGroup);
    }

or the saveHandler:

  public saveHandler({ sender, rowIndex, formGroup, isNew }) {

    const ifastenerTypeDimension: IFastenerTypeDimension = formGroup.value;

    this.fastenerTypeDimensionService.save(ifastenerTypeDimension, isNew);

    sender.closeRow(rowIndex);
    this.ngOnInit();
  }

As I mentioned before with different combinations of valueChange, valueNormalizer, [formControl, and [allowCustom], I can either generate a new DimensionType, save a combobox value to my back end, but I can't successfully do both and when I split the difference I get some version of one or the other with more errors.

 

 

0
Dimiter Topalov
Telerik team
answered on 06 Mar 2018, 09:38 AM
Hello Ron,

It seems that when the Grid row is no longer in editing mode and the new custom value, coming from the updated ComboBox data needs to be displayed, the Grid data is still not "aware" of the performed change and cannot match the new value of the ComboBox with the text that needs to be displayed.

Depending on what mechanics are used to display the custom field (for example is it a method call within a cell template as in the discussed runnable sample?), and also on whether there are separate services for the ComboBox data and the Grid data, or a single service is responsible for managing all data changes, the approach may vary.

In general, the flow of events for the scenario to work as expected is as follows:

1) The Grid row is put in editing mode and a custom value is entered in the ComboBox field

2) The ComboBox data collection is updated so that it contains the new item

3) The Grid data is updated so that the respective Grid data item contains the foreign key to the new ComboBox item

4) Given 2) is finished successfully, the text, corresponding to the new ComboBox value should be displayed in the Cell template

From the provided code snippets it seems that an asynchronous call to the service, updating the ComboBox data is performed, and it is still not finished when the edited row is closed. Thus the Grid cannot match the new value (foreign key) to the corresponding text, as there is still no such data item in the ComboBox's data collection.

https://plnkr.co/edit/In0CpGNXeQ1ThiDWwqsQ?p=preview

Check the updated example - the Grid is blank until the collection it relies on for retrieving the text value for the Category field is actually updated.

As mentioned, the approach may vary depending on the specifics of the scenario, but assuming that you are using a similar approach for retrieving the text that needs to be displayed in the respective Grid column by extracting it from a collection of complex objects, based on the GUID, you will need to update this collection on the client so that it contains the newly added custom value during editing. This can be done by reading the remote service or manually updating the already present collection.

I hope this makes sense, but if the issue persists, we will need a similar isolated runnable example or project where the problem can be observed, so we can investigate it further, and provide a suggestion best suitable to the specifics of the scenario. Thank you in advance.

Regards,
Dimiter Topalov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 07 Mar 2018, 03:37 PM

Dimiter,

One quick question going back to my first post, is there a specific way I should configure my API's in the kendo-combobox html tag with this configuration? If I can get that piece down I'll at least know that my combobox setup/inputs should work and I can troubleshoot my code further from there.

Thanks!

0
Dimiter Topalov
Telerik team
answered on 08 Mar 2018, 02:08 PM
Hello Ron,

The ComboBox configuration, provided in the first post seems correct, it just can be modified such that the allowCustom property is bound to the isNew boolean, available in the edit template:

<ng-template kendoGridEditTemplate
              let-dataItem="dataItem"
              let-column="column"
              let-formGroup="formGroup"
              let-isNew="isNew">
              <kendo-combobox
                [allowCustom]="isNew"
                [valueNormalizer]="valueNormalizer"
                #ddl="popupAnchor"
                popupAnchor
                [data]="categories"
                textField="CategoryName"
                valueField="CategoryID"
                [valuePrimitive]="true"
                [formControl]="formGroup.get('CategoryID')"
              >
              </kendo-combobox>

This way custom values will be allowed for newly added items only, and not when editing existing ones:

https://plnkr.co/edit/WAcMQE8VxPxXZF2m4NuD?p=preview

All other custom logic for making the scenario work depends on handling asynchronous updating of the collection the ComboBox is bound to, and the one the Grid is bound to. If as described in previous posts, the actual value of the Grid data item property the ComboBox is bound to via formControl is updated, this means that the setup is correct, and displaying the text, corresponding to this new value in the Grid cell template is a matter of the item being present in the collection of objects where it is obtained from via the custom method.

In the discussed example this is performed as follows:

<ng-template kendoGridCellTemplate let-dataItem>
              {{category(dataItem.CategoryID)?.CategoryName}}
            </ng-template>

public category(id: number): any {
        return this.categories.find(x => x.CategoryID === id);
    }

When the "categories" collection is updated with the new data item, created as a custom value in the ComboBox, the respective text is displayed in the Grid. 

Regards,
Dimiter Topalov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 08 Mar 2018, 11:11 PM

Dimiter,

Thank you - that approach worked. I also wrapped the return in the valueNormalizer with a setTimeout which covered the processing gap.

The next step will be figuring out how to hold onto that new value as selected.

0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 09 Mar 2018, 03:10 PM

One question that might be work asking regarding the topic immediately above,

Is [value] or [(ngModel)] supposed to be able to hook into the intermediate (currently blank) placeholder that shows up after I enter a new custom value? I noticed that when I've tried so far to inject that value through these methods it just seems to handle the very first value that shows up when I open the grid. 

I guess my question with respect to that - what is the blank box that shows up after a new value is added and is it something that I still just need to work out timing on with the above methods (such as value or ngModel) or is it the actual form? I remember finding ways where I was able to inject that value in as the new starting value but I found strangely that a) I couldn't save the grid line and b) I duplicated my creation request. I'm guessing there's a way around this, I'm just not sure what.

0
Dimiter Topalov
Telerik team
answered on 12 Mar 2018, 11:47 AM
Hello Ron,

I am not sure I fully understand the question, but in the discussed scenario, the Grid displays a text value, based on the primitive value, that the ComboBox holds that is used to match the primitive value (e.g. 1) to the text, corresponding to the object with CategoryID 1 in the list of ComboBox data items (e.g. "Beverages").

The Grid column is bound to a field on the Grid data item (CategoryID) and uses the Cell template to display a text value, based on the number the CategoryID property holds. To do so, a function that iterates through the ComboBox data items and returns the text, matching to this CategoryID, is called.

The reason for the Grid being empty after a custom value is added to the ComboBox data, is that the CategoryID field of the Grid data item is updated and holds a newly created value (e.g. 101), but the function that should return the corresponding text does not find such an item in the ComboBox data, thus no text appears. The ComboBox's value is 101, and so is the CategoryID of the Grid data item (e.g. editing has worked as expected), but the text of the newly added ComboBox data item is not displayed in the Grid due to the previously discussed timing issues.

This data binding mechanism is separate and different from the placeholder mechanism - the placeholder is just a text that will be displayed in the ComboBox when the component has no value. However, in the discussed scenario the value of the ComboBox and the respective Grid data item field is updated successfully, and the "blank" is displayed in the Grid cell (not in the ComboBox) due to the reasons, described above.

The Grid editing relies on building a Reactive or Template-driven form (based on the scenario requirements), and used wither [(ngModel)] binding for Template-driven forms or formGroup/formControl binding for Reactive forms. The [(value)] binding of the ComboBox is an alternative mechanism for binding the component's value, but is not related to the discussed custom Grid editing.

I hope this helps, but if I am missing something, please elaborate in further details on the parts that remain unclear.

Regards,
Dimiter Topalov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 12 Mar 2018, 02:32 PM

Dimiter,

Thank you for the depth of that response and yes it was helpful.

To verify my situation is slightly different than what's happening with the Plunker you sent. With the Plunker if I create a new value (eg. { "CategoryID": 100, "CategoryName": "Cleaning Supplies" } ) and update/save I get a blank cell where that value. In my own grid I get the value to show up on save, it's just that when the new value is created in the combobox the user has to go back through the combobox to look for it and select it after creation. From that standpoint the field I'm thinking of trying to send the value back to might be best described as the grid editor or in-cell editor related to the selected value for the combobox and I've discovered, as you mentioned, that the starting/default value is something slightly different than the blank that shows up in the combobox after the creation of a new value.

My first step in trying to solve this was making a .push directive to add the new object into the combobox array, that might still be a good idea but it doesn't hook into anything that would cause the new value to be autoselected once created. There was a previous workaround in jquery (via ASP.Net) with a reference to .data().kendoGrid.datasource.data()[index] which we were using for this purpose and I'm hoping there might be an equivalent action I can take within the @progress/kendo-angular libraries to get the same result.

0
Dimiter Topalov
Telerik team
answered on 14 Mar 2018, 12:08 PM
Hi Ron,

Thank you for describing the issue in further details. It may be caused by the fact that when valuePrimitive is set to true, the ComboBox needs to be bound to a primitive value - in this case the CategoryID, so this is the value that needs to be returned by the value normalizer function. Here is an example where the whole newly created object is returned that is not working properly:

public valueNormalizer = (text) => text.pipe(map((text: string) => {
      const newItem = {
          CategoryID: this.nextId,
          CategoryName: text
      };
       
      this.nextId++
       
      if(!this.categories.some(cat => cat.CategoryName === newItem.CategoryName)) {
        this.categories.push(newItem);
      }
       
      return newItem; // primitive value expected, but the whole object is returned instead
    }));

https://plnkr.co/edit/0frSO5gU4getlvgE5ySl?p=preview

However when a primitive value is returned, the example works as expected:

public valueNormalizer = (text) => text.pipe(map((text: string) => {
      const newItem = {
          CategoryID: this.nextId,
          CategoryName: text
      };
       
      this.nextId++
       
      if(!this.categories.some(cat => cat.CategoryName === newItem.CategoryName)) {
        this.categories.push(newItem);
      }
       
      return newItem.CategoryID; // primitive value returned
    }));

https://plnkr.co/edit/pGcENYXu0b9CRdTLyqkQ?p=preview

Please make sure that the valueNormalizer function has a similar functionality on your end too.

As for manipulating the DataSource approach, used for the Kendo UI for jQuery Grid, it would not be applicable to the Kendo UI for Angular Grid as there is no DataSource anymore - the Grid is bound directly to the collection of data items (or an observable containing them). Further details are available in the respective Data Binding section of our documentation:

https://www.telerik.com/kendo-angular-ui/components/grid/data-operations/data-binding/

That said, the data collection the Grid is bound to is available on the client and can be manipulated directly (although typically this is not necessary, and it is recommended all necessary updates to go through a service as a result of hanling the Grid editing-related events).

If some of the discussed issues persist, we will need an isolated runnable project similar to the ones above, where we can observe them, so we can inspect it and determine what could be the most suitable approach for resolving them, depending on the specific scenario.

Regards,
Dimiter Topalov
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Ron
Top achievements
Rank 1
Iron
Iron
Iron
answered on 15 Mar 2018, 04:25 PM

Dimiter,

TY - the advice on returning just the primary key was exactly what I needed. It's holding the new combo box option now.

Tags
General Discussions
Asked by
Ron
Top achievements
Rank 1
Iron
Iron
Iron
Answers by
Dimiter Topalov
Telerik team
Ron
Top achievements
Rank 1
Iron
Iron
Iron
Share this question
or