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

Use MultiColumnComboBox as a cell editor

9 Answers 174 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Patrick
Top achievements
Rank 1
Veteran
Patrick asked on 17 Feb 2021, 07:06 AM

I've found examples how to use the kendo dropdown list as a editor in a column. Can you please provide me an example of how to use a MultiColumnComboBox as a column editor and how you would populate the MultiColumnComboBox in such a use case?

Thank you.

9 Answers, 1 is accepted

Sort by
0
Ivan Danchev
Telerik team
answered on 18 Feb 2021, 05:28 PM

Hello Patrick,

The following demo: https://demos.telerik.com/aspnet-mvc/grid/editing-custom shows how to use a DropDownList as a cell editor in the column bound to the Category field:

columns.Bound(p => p.Category).ClientTemplate("#=Category.CategoryName#").Sortable(false).Width(180);

The demo has a View Source tab, in which you can see the editor template (ClientCategory.cshtml), in which the DropDownList is declared:

@(Html.Kendo().DropDownListFor(m => m)
        .DataValueField("CategoryID")
        .DataTextField("CategoryName")
        .BindTo((System.Collections.IEnumerable)ViewData["categories"])
)

A MultiColumnComboBox can be used in place of the DropDownList, without any other changes to loading the data or to the Grid's configuration:

@(Html.Kendo().MultiColumnComboBoxFor(m => m)
	.DataValueField("CategoryID")
	.DataTextField("CategoryName")
	.BindTo((System.Collections.IEnumerable)ViewData["categories"])
	.Columns(columns =>
	{
		columns.Add().Field("CategoryName").Title("Name");
		columns.Add().Field("CategoryID").Title("ID");
	})
)

 

Regards,
Ivan Danchev
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Patrick
Top achievements
Rank 1
Veteran
answered on 24 Feb 2021, 04:44 PM

Thank you for the answer, Ivan. For the most part, I have it working: The UIHint in the view model didn't work so I assigned the EditorTemplateName property, ProgramMultiColumnComboList, in the column binding.

Here's my editor shared view, named ProgramMultiColumnComboList:

@(Html.Kendo().MultiColumnComboBoxFor(m => m)
    .Placeholder("<Select a Program>")
    .DataValueField("ProgramId")
    .DataTextField("ProgramCode")
    .BindTo((System.Collections.IEnumerable)ViewData["programList"])
    .Columns(columns =>
    {
        columns.Add().Field("ProgramCode").Title("Code").Width("75px");
        columns.Add().Field("RevenueSource")
            .Title("Src")
            .HeaderTemplate("<div style='text-align:center;'>Src</div>")
            .Width("55px");
        columns.Add().Field("ProgramName").Title("Program Name").Width("375px");
    })
    .HtmlAttributes(new { style = "width:100%;" })
    .Filter("contains")
    .FilterFields(new string[] { "ProgramCode", "ProgramName" })
    .AutoBind(true)
)

This is the column binding in the view:

columns.Bound(c => c.ProgramId).Title("Program")
                        .ClientTemplate("#=ProgramCode#")
                        .EditorTemplateName("ProgramMultiColumnComboList")
                        .Width("115px");

 

It's working in so far as the MultiColumnComboBox appears in the cell and is populated as expected. However, I am using the InCell edit mode and .Batch(true). So what is happening when I select a value from the list, the ProgramId is getting assigned as it should but the ProgramCode property is not getting assigned the ProgramCode from the list. So when I leave the cell, it shows the edit flag in the corner, but displays the old program code when updating or null when adding. Since the program ID is assigned, it saves appropriately and gets the Program Code from the related table in the controller's read method. 

So, can you tell me if there's an easy way to updated the ProgramCode property referenced in the ClientTemplate property? Do I need to capture some event when an item is selected? If so, what event and how to I get the ProgramCode from the selected item and put it in the grid's current view model item's ProgramCode?

Thanks again.

Patrick

 

0
Ivan Danchev
Telerik team
answered on 26 Feb 2021, 11:27 AM

Patrick,

Consider updating the ProgramCode field value programmatically. For example:

1. You could attach a Select event handler to the MultiColumnComboBox. In its handler you have information about the selected item and its ProgramId and ProgramCode values.

2. Get the data item of the edited row in the Grid. The Grid has a dataItem method, that returns the data item object. It in turn has a set method, which you can use to set the value obtained from the ProgramCode field of the MultiColumnComboBox' selected item to the ProgramCode field of the Grid's data item. For example: dataItem.set("ProgramCode", myNewValue);

Regards,
Ivan Danchev
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Patrick
Top achievements
Rank 1
Veteran
answered on 27 Feb 2021, 08:10 PM

Thanks again for the reply, Ivan. We're on the same page in that the questions in my last reply were getting at how I can update the grid to reflect the choice in the multicolumncombobox (MCCB) in the template partial view.

So for your paragraph 1: The problem I am having is hooking into the event. (BTW: the select event doesn't work for me since when it gets the point of reading anything from my dataItem variable, I get an error "Uncaught TypeError: Cannot read property 'ProgramCode' of undefined at init.multiComboChange" but I can read from dataItem when using the change event for a MCCB on the main page).

I am able to successfully assign an event handler in document ready to a MCCB locally on the page. Here's the event handler assigned to a variable:

var multiComboChange = function (e) {
    var value = e.sender.value() || 0;
    var text = e.sender.text();
    var dataItem = e.sender.dataItem();
 
    console.log("ID:" + value + "\r\n" + text);
    console.log("Code:" + dataItem["ProgramCode"]); // <-- When using as a select event handler,
                                                       //     this produces error: Uncaught TypeError:
                                                       //     Cannot read property 'ProgramCode' of
                                                       //     undefined at init.multiComboChange...
                                         //      But using as a change event handler works.
     
    console.log("Put this prg code in grid!");
};

 

 

And here's how the handler is assigned to a local MCCB inside of $(document).ready:

var multicolumncombobox = $("#SubBonds").data("kendoMultiColumnComboBox");
multicolumncombobox.bind("change", multiComboChange);

 

But doing that same thing for the MCCB that is located in the shared view/template...

var multicolumncombobox = $(".programTemplate").data("kendoMultiColumnComboBox");
multicolumncombobox.bind("select", multiComboChange);

 

 

... produces the error "Cannot read property 'bind' of undefined at HTMLDocument.<anonymous>" at the multicolumncombobox.bind line. You'll notice I used a class selector, .programTemplate assigned to the MCCB html attributes in the template file. I also tried the #ProgramId selector since in examining the page source that the MCCB has the ID of the bound grid column.

I also tried the code below inside of $(document).ready:

$(document).on("change", "#SubBonds", function(e){
    alert("Changed");
 
    var value = e.sender.value();  // <-- Uncaught TypeError: Cannot read property 'value' of undefined at HTMLInputElement.<anonymous> (Index:258)
    var text = e.sender.text();
    var dataItem = e.sender.dataItem();
    var prgCode = dataItem("ProgramCode");
    console.log("Prg Code: " + prgCode);
});

 

That actually fired when a selected an item in the MCCB, however as soon as it hit the line "var value = e.sender.value();" it produced this error: "Uncaught TypeError: Cannot read property 'value' of undefined at HTMLInputElement.<anonymous>"

The latter error occurred whether the selector was to the local #SubBonds or the template assigned to #ProgramId.

So, can you tell me how to hook the change event of the MCCB that's inside the shared view so I can get a column value to place in the grid?

 

0
Patrick
Top achievements
Rank 1
Veteran
answered on 27 Feb 2021, 08:24 PM

Regarding your paragraph 2:

2. Get the data item of the edited row in the Grid. The Grid has a dataItem method, that returns the data item object. It in turn has a set method, which you can use to set the value obtained from the ProgramCode field of the MultiColumnComboBox' selected item to the ProgramCode field of the Grid's data item. For example: dataItem.set("ProgramCode", myNewValue);

In my 02/24 reply I showed how I bind to my ProgramId field:

columns.Bound(c => c.ProgramId).Title("Program")
                        .ClientTemplate("#=ProgramCode#")
                        .EditorTemplateName("ProgramMultiColumnComboList")
                        .Width("115px");

As you can see in the above, ProgramCode is referenced as the ClientTemplate for the ProgramId column. I do not have a separate columns.Bound() for ProgramCode since it's displayed in the ProgamId column whether or not a multicolumncombobox is showing. Since there's not a separate bound ProgramCode column, will there be a ProgramCode field in the dataItem? I'm thinking not and if that's so, how will I get the selected program code to appear in the ProgramId column when the multicolumncombobox is not displayed?

Thanks again.

Patrick

0
Patrick
Top achievements
Rank 1
Veteran
answered on 01 Mar 2021, 04:37 AM

A couple of posts back I concluded asking "So, can you tell me how to hook the change event of the MCCB that's inside the shared view so I can get a column value to place in the grid?" 

I realized, when inspecting the page in the browser developer tools, that it may not be an issue of the MCCB being in a template/shared partial view. Looking at the elements during an edit of a ProgramID, I noticed that the kendo.syncReady #ProgramId.kendoMultiColumnComboBox is there only when a cell is being edited as illustrated in the attached image. So maybe it is an issue of the MCCB being an editor in the grid. Regardless, I still have the same problem: how to capture its change event where I can read the dataItem field values. But I see as shown in the screen capture, it's different than a MCCB as a standalone input on the page because it seems like it's being created dynamically when the field is modified and therefore doesn't exist during document ready. I look forward to your advice.

 

 

But

0
Ivan Danchev
Telerik team
answered on 02 Mar 2021, 02:56 PM

Patrick,

In order to access a field (e.g., ProgramCode) in a column's ClientTemplate, this field must be present in the Grid's data. Whether there is a column bound to that field or not is irrelevant. Here's an exemplary response showing exemplary data of 3 records:

{
   "Data":[
      {
         "ProductID":1,
         "ProductName":"Chai",
         "UnitPrice":18.0,
         "UnitsInStock":39,
         "Discontinued":false,
         "LastSupply":"2021-03-02T00:00:00+02:00",
         "UnitsOnOrder":0,
         "Category":{
            "CategoryID":1,
            "CategoryName":"Beverages"
         },
         "CategoryID":1,
         "QuantityPerUnit":"10 boxes x 20 bags"
      },
      {
         "ProductID":2,
         "ProductName":"Chang",
         "UnitPrice":19.0,
         "UnitsInStock":17,
         "Discontinued":false,
         "LastSupply":"2021-03-02T00:00:00+02:00",
         "UnitsOnOrder":40,
         "Category":{
            "CategoryID":1,
            "CategoryName":"Beverages"
         },
         "CategoryID":1,
         "QuantityPerUnit":"24 - 12 oz bottles"
      },
      {
         "ProductID":3,
         "ProductName":"Aniseed Syrup",
         "UnitPrice":10.0,
         "UnitsInStock":13,
         "Discontinued":false,
         "LastSupply":"2021-03-02T00:00:00+02:00",
         "UnitsOnOrder":70,
         "Category":{
            "CategoryID":2,
            "CategoryName":"Condiments"
         },
         "CategoryID":2,
         "QuantityPerUnit":"12 - 550 ml bottles"
      }
   ],
   "Total":77,
   "AggregateResults":null,
   "Errors":null
}

So ProductID, ProductName, etc. are the fields that can be accessed and thus displayed in the ClientTemplate. If you don't have the ProgramCode field in the data, you cannot access it in the template.

I will get back to our demo and explain the difference between how the custom editor is used in it compared to the snippet you posted:

columns.Bound(c => c.ProgramId).Title("Program")
                        .ClientTemplate("#=ProgramCode#")
                        .EditorTemplateName("ProgramMultiColumnComboList")
                        .Width("115px");

In your case the column is bound to ProgramId which I assume is of type int or string.

In the demo the column is bound to an object:

columns.Bound(p => p.Category).ClientTemplate("#=Category.CategoryName#").Sortable(false).Width(180);

The Category field in the main model:

[UIHint("ClientCategory")]
public CategoryViewModel Category
{
    get;
    set;
}

is of type CategoryViewModel, i.e. it is a separate view model with its own CategoryId and CategoryName properties:

public class CategoryViewModel
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
}

You can see the sample response posted above to see how it comes in the data.

Using this approach allows accessing specific field in the template and displaying its value in the column. The DropDownList or MultiColumnComboBox used as an editor is bound to the CategoryId, but both CategoryID and CategoryName are accessible through Category, so the displayed value is that of CategoryName. Note how it is accessed in the template in the column's configuration posted above (highlighted in green).

If you choose to go with this approach you won't have to use the previously suggested "change" event handler.

Regards,
Ivan Danchev
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Patrick
Top achievements
Rank 1
Veteran
answered on 05 Mar 2021, 04:54 PM

Ivan,

Thank you for sticking w/me on this and for the advice. I was trying to avoid having to change my model for the purpose of a UI presentation requirement. However, I made the change for my Program dropdown list and I think it's working. Running into a different challenge on a second column that is another MultiColumnComboBox for the editor template. However, the model for this second list includes a date as one of the columns to be displayed. Here is a snippet that populates the object (MaturityListItem) which itself is a property of another object (SubBondDebtStructure) being populated in the snippet. 

MaturityItem = new MaturityListItem()
{
    MaturityId = debtStructure.Maturity.MaturityId,
    BondId = debtStructure.Maturity.BondId,
    CUSIP_Issue = debtStructure.Maturity.CUSIP_Issue,
    MaturityDate = debtStructure.Maturity.MaturityDate,
    Principal = debtStructure?.Principal ?? 0m,
    CouponRate = debtStructure.Maturity.CouponRate
};

From the above you can see the properties of MaturityListItem. Here's a snippet of my SubBondDebtStructure model in which MaturityListItem is a property:

[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
[Display(Name = "Trial Date")]
public DateTime TestDate { get; set; }
public int MaturityId { get; set; }
public MaturityListItem MaturityItem { get; set; }
public int ProgramId { get; set; }

So the first problem I ran into is the fact that when my collection of SubBondDebtStructures result was sent to the view via:
return Json(viewModelDebtStructures.ToDataSourceResult(request));

the MaturityDate property used as a client template in columns.Bound(c => c.MaturityItem).ClientTemplate("#= MaturityItem.MaturityDate#") was being displayed as /Date(1682917200000)/. I found a solution for that via this forum post. So using the client template below works for display: 

columns.Bound(c => c.MaturityItem).ClientTemplate("#= MaturityItem.MaturityDate ? kendo.toString(kendo.parseDate(MaturityItem.MaturityDate), 'MM/dd/yyyy') : '' #");

But as you can see by the attached screen capture, the underlying value is still /Date(ticks value)/ so any attempt to save produces the error "The value '/Date(1682917200000)/' is not valid for MaturityDate." So now I'm seeking your advice how to use a list of objects that includes a date column/property that needs to be displayed in the MultiColumnComboBox and the list needs to be sorted by the date. 

Thanks again.

Patrick

0
Ivan Danchev
Telerik team
answered on 08 Mar 2021, 04:38 PM

Patrick,

The Grid does not recognize the type of the nested field because the dataSource schema does not support complex objects (in this case MaturityItem.MaturityDate). My colleague Vladimir has replied in another forum thread where a similar scenario is discussed, see https://www.telerik.com/forums/date-time-issue#3DbnawWNFk-QrE-n1z29Bw

Regards,
Ivan Danchev
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
Grid
Asked by
Patrick
Top achievements
Rank 1
Veteran
Answers by
Ivan Danchev
Telerik team
Patrick
Top achievements
Rank 1
Veteran
Share this question
or