I'm new to using Telerik, and I am currently working on a ASP.Net Core Grid
So I have it working, but now I want to add the search bar toolbar, so I added it as:
@(Html.Kendo().Grid(Model)
.Name("grid")
.ToolBar(t => t.Search())
.Search(s =>
{
s.Field(o => o.PrimaryContact.FirstName, "contains");
})
The problem is that PrimaryContact.FirstName
can be null, so when I try to search for something, it throws a console error:
Uncaught TypeError: Cannot read properties of null (reading 'FirstName')
This same thing happen on the columns and I solve it adding ClientTemplate
as:
columns.Bound(x => x.PrimaryContact.FirstName)
.ClientTemplate("#= PrimaryContact ? PrimaryContact.FirstName : '' #")
But Search has no a client template to handle this
I tried to change the null value for the string empty with jQuery as:
function onDataBound(e) {
for (let i = 0; i < rows.length; i )
{
const row = $(rows[i]);
const dt = e.sender.dataItem(row);
const firstName = dt.get("PrimaryContact.FirstName");
if (firstName === null)
{
row[0].cells[2].innerHTML = "";
}
}
}
But it does not work.
How can I avoid the null values from the search? Regards
FullCode:
@(Html.Kendo().Grid(Model)
.Name("grid")
.ToolBar(t =>
{
t.Excel();
t.Pdf();
t.Search();
})
.DataSource(dataSource => dataSource
.Custom()
.PageSize(20)
.Schema(schema =>
{
schema.Data(x => "function(d) { return validateData(d); }");
})
)
.Pageable(pager => pager
.Numeric(true)
.Info(true)
.PreviousNext(true)
.PageSizes(new [] { 10 ,25 ,50 })
.Position(GridPagerPosition.Bottom)
.Messages(m =>
{
m.ItemsPerPage("entries");
m.Display("Showing {0} - {1} of {2} entries");
})
)
.Sortable()
.Search(s =>
{
s.Field(o => o.Name, "contains");
s.Field(o => o.PrimaryContact.FirstName, "contains");
s.Field(o => o.PrimaryContact.PhoneNumber, "contains");
s.Field(o => o.PrimaryContact.EmailAddress, "contains");
s.Field(o => o.IsActive, "contains");
})
.Events(events => events.DataBound("onDataBound"))
.Columns(columns =>
{
columns.Bound(x => x.AdvertiserId)
.Hidden();
columns.Bound(x => x.Name)
.Title("Advertiser Name");
columns.Bound(x => x.PrimaryContact.FirstName)
.ClientTemplate("#= PrimaryContact ? PrimaryContact.FirstName : '' #")
.Title("Contact Name");
columns.Bound(x => x.PrimaryContact.PhoneNumber)
.ClientTemplate("#= PrimaryContact ? PrimaryContact.PhoneNumber : '' #")
.Title("Contact Phone");
columns.Bound(x => x.PrimaryContact.EmailAddress)
.ClientTemplate("#= PrimaryContact ? PrimaryContact.EmailAddress : '' #")
.Title("Contact Email");
columns.Bound(x => x.IsActive)
.Title("Status")
.ClientTemplate("<span class='badgeTemplate'></span>")
.Sortable(false);
columns.Template("<div class='btn-group'></div>")
.Title("").Width(100);
})
)
<script type="text/javascript">
function validateData(data)
{
for (const item of data) {
if (!item.hasOwnProperty("PrimaryContact")) {
item["PrimaryContact.FirstName"] = { "FirstName": ""};
}
}
return data;
}
</script>
I also tried using BeforeEdit event
.Events(events => events.DataBound("onDataBound").BeforeEdit("onBeforeEdit")
function onBeforeEdit(e) {
if(e.model.isNew()){
e.PrimaryContact.FirstName = "";
}
}
But it does not work as well
CodePudding user response:
Probably avoid using nested properties whenever possible. You should have a custom ViewModel that flattens everything (whatever your Model
class is). Second, the asp.net API is not as full featured as the native javascript library. It's just a passthrough to generate javascript on the page anyway, so don't be afraid to add javascript if you need to.
What you're trying to do isn't possible. You can build a proper flat ViewModel, or you can intercept data and mutate before loading it into the grid. You do that using the schema
property on the datasource. You'll define a custom javascript method.
One more thing, I think your problem is not that FirstName
is missing, it's that PrimaryContact
is null. You can probably fix this server side without too much hassle. But here's how to fix it client side, if, for example, you're getting this from a 3rd party API.
.DataSource(ds =>
{
ds
.Custom()
.Transport(t =>
{
// ...
})
.Schema(x => .Data(x => "function(d) { return validateData(d); }"))
// ...
;
})
Or if you just use the javascript definition
dataSource: {
// ...
schema: { data: validateData }
}
Then here is the javascript method to cleanup your model with whatever properties you need to fix:
function validateData(data)
{
for (const item of data) {
if (!item.hasOwnProperty("PrimaryContact")) {
item["PrimaryContact"] = { "FirstName": ""};
}
}
return data;
}
Full dojo example here