Hello,
I have a grid with derived objects and I would like to open different edit popup for each type of child object.
I'll give a simplified example:
Models:
public class Product
{
public string Label { get; set; }
}
public class TypeA: Product
{
public string PropA { get; set; }
}
public class TypeB: Product
{
public int PropB { get; set; }
}
public class Order
{
public int Id {get;set;}
public virtual ICollection<
Product
> Products { get; set; }
}
Controller:
public
async Task<Order> GetById(
int
id)
{
Order order = await webApi.GetById(id);
// webApi is a private service who calls a json converter to return a strongly typed list with derived class
return
order ;
}
At this point, the object order have a Products property which is a collection of TypeA, TypeB and Product
Views:
@model Order
<
div
>
@(Html.Kendo().Grid<
Product
>(Model.Products).Name("grid")
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.Columns(columns =>
{
columns.Bound(p => p.Label);
columns.Command(command => { command.Edit(); });
})
)
</
div
>
I also create templates in Views\Shared\EditorTemplates
@model Product
<
div
>
<
h1
>Product</
h1
>
</
div
>
-------------------------
@model TypeA
<
div
>
<
h1
>TypeA</
h1
>
</
div
>
-------------------------
@model TypeB
<
div
>
<
h1
>TypeB</
h1
>
</
div
>
Only the Product template pops up. I guess it's because of Grid<Product> declaration.
How can I strongly typed each row and/or have different editor template? I don't want to show PropA and PropB in my grid but I want to allow to edit them in the popup.
Thanks
7 Answers, 1 is accepted
Generally speaking. the grid creates the editor form based on the specified model - in your case Product. Therefore it is expected that the popup contains only the fields of the Product class.
As a workaround I can suggest you to create a conditional popup and depending on the current item, display the relevant fields.
e.g.
// override default editor
$(
function
() {
var
grid = $(
'#grid'
).data(
'kendoGrid'
);
var
options = grid.options;
options.editable.template = $(
"#template-edit"
).html();
// replace server generated editor with client-side editor
grid.setOptions(options);
})
// editor
<script type=
"text/x-kendo-template"
id=
"template-edit"
>
#if(data.PropA) {# // if the item has prop A it is of type TypeA
#var tempA = kendo.template($("\#typeAEditor").html());#
#=tempA(data)#
#} else {#
#var tempB = kendo.template($("\#typeBEditor").html());#
#=tempB(data)#
#}#
</script>
// type a and type b editors
<script type=
"text/x-kendo-template"
id=
"typeAEditor"
>
@Html.Partial(
"TypeAEditor"
)
// the partial view which contains the editor for Type A
</script>
<script type=
"text/x-kendo-template"
id=
"typeBEditor"
>
@Html.Partial(
"TypeBEditor"
)
// the partial view which contains the editor for Type B
</script>
However, I am not quite sure how creating items will be handled. Are the users allowed to create new items?
Regards,
Georgi
Progress Telerik

Hello,
That's what I thought.
When I was wating for your answer I found an other solution which works fine.
I bound an ajax function to a custom columns.command. The controller select the good view to return and then I bound the dataItem from the grid to it.
$.ajax({
headers: {
'Accept'
:
'application/json'
,
'Content-Type'
:
'application/json'
},
url:
this
.url,
type:
"post"
,
data: JSON.stringify(
this
.data),
dataType:
"html"
,
success: (response) => {
this
.window = $(div).kendoWindow({
modal:
true
,
title:
this
.data[
"Discriminator"
],
open: (e: kendo.ui.WindowEvent) => {
this
.window.center();
},
}).data(
"kendoWindow"
);
this
.window.content(response);
kendo.bind(this.window.element, this.data);
}
})
this.data contains the discriminator (from entity framework). I send the full object, but I guess I can only pass the discriminator in a get request.
the controller:
[HttpPost]
public
IActionResult Edit([FromBody] Product product)
{
string
name = product.GetType().Name;
string
viewName =
new
StringBuilder(name).Append(
"Edit"
).ToString();
return
PartialView(viewName);
}
I created view for each type with data-bind attribute like this:
<
textarea
data-role
=
"editor"
data-bind
=
"value:Label"
></
textarea
>
Now I just need to create the valid and cancel command =)
Thank you for sharing your solution with the community.
Indeed another approach would be to create your own popup editor. Have in mind that you will have to manually refresh the grid to display the changes made through the custom window.
Regards,
Georgi
Progress Telerik

Hi,
I do not even need to manage the refresh because of the kendo.bind().
But I guess I will have to if I need to improve the dirty tracking
Sometimes the items are modified on the server, for example when creating a new record, most commonly the id is assigned on the server. In such cases, to display the latest state of the data in the grid, you will have to refresh it as the client is not aware of the modifications done on the server.
Regards,
Georgi
Progress Telerik

You're right, I add a function to set each server response property to keep dirty tracking
so instead of something like this
$.extend(data,serverResponse);
I have a function
private dataSetter(data: kendo.data.Model): void {
let keys = Object.keys(data);
for
(let i = 0; i < keys.length; i++) {
let value = data[keys[i]];
let currentValue =
this
.viewModel.get(`data.${keys[i]}`);
if
(currentValue != value) {
this
.viewModel.set(`data.${keys[i]}`, value);
}
}
}
Thank you for sharing your solution with the community.
I have examined the provided function for syncing the data and I did not notice anything that might cause an issue.
Regards,
Georgi
Progress Telerik