I just want to display a GUID from my model in a Kendo grid in the view, but I am getting this error each time:
The model item passed into the dictionary is of type 'System.Guid',
but this dictionary requires a model item of type 'System.String'.
This is my grid code:
#(Html.Kendo().Grid(Model.revisions)
.Name("RevisionsGrid")
.Columns(columns =>
{
columns.Bound(p => p.RevisionInfo.Id).Title("Revision ID");
columns.Bound(p => p.Accident.Id).Title("Accident ID")
.ClientTemplate("#= (Accident.Id == null) ? '' : Accident.Id.toString() #");
columns.Bound(p => p.RevisionInfo.RevisionDate).Title("Date Modified");
columns.Bound(p => p.RevisionInfo.User.Name).Title("By User");
columns.Command(command => command.Custom("ViewPdf").Text("View PDF").Click("getPdf"));
})
.Selectable()
.Pageable(p => p.PageSizes(new[] { 5, 10, 25 }))
.DataSource(dataSource => dataSource
.Server()
.Model(model =>
{
model.Id(p => p.RevisionInfo.Id);
model.Field(p => p.Accident.Id).DefaultValue(Guid.NewGuid());
}))
The problem is with the second column which is bound to Accident.Id, a GUID type. My attempts at fixing this included adding the ClientTemplate to that column & adding a default value for it, but neither of these helped. I also tried just adding ToString() in the column definition but then I get a different error about .Bound() only taking a property access method as an argument.
Is there some place to convert the GUID to a string in order to display it here?
I figured it out. In order to have the Kendo grid accept something that's not a string you need to specify a template for the column, in which you can call ToString(). I couldn't call it in binding.
columns.Bound(p => p.Accident.Id).Title("Accident ID")
.Template(p => p.Accident.Id.ToString());
Related
I've just started using Telerik, to speed up some of my development, however I'm stuck trying to bring up a normal MVC Details page from a Grid Component.
All I want, is a select button to send the user to something like /Person/Details/5
The reason why I want a separate page (rather than a popup) is that some of the data relationships are complex, and it's better experience that these are displayed in it's own page, rather than a popup.
My model has a few objects, that's why you see the p.person.Title type setup below.
#(Html.Kendo().Grid(Model)
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.person.Title).Width(100);
columns.Bound(p => p.person.Firstname);
columns.Bound(p => p.person.Firstname);
columns.Bound(p => p.person.Surname);
columns.Bound(p => p.person.ABN).Width(210);
columns.Bound(p => p.person.PracticeCode);
columns.Bound(p => p.currentform);
columns.Command(command => { command.Edit(); });
columns.Command(command => { command.Destroy(); });
columns.Command(command => { command.Select(); });
})
.Sortable()
.ToolBar(commands => commands.Create())
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.DataSource(dataSource => dataSource
.Server()
.Model(model => model.Id(p => p.person.personkey))
.Create(create => create.Action("Create", "Person"))
.Read(read => read.Action("Details", "Person" ))
.Update(update => update.Action("Update", "Person"))
.Destroy(destroy => destroy.Action("Destroy", "Person"))
The grid itself is working fine, however, when someone selects the "Select" button, I want them to go to the /Person/Details/{id} page. Telerik is almost doing it right, it's sending the user to
/Person/Details?grid-mode=select&person.personkey=11 (I actually want /Person/Details/11 )
So, I noticed that the "Read" method has a few different signatures to enable route values, but I just can't seem to rig it up to get the right route values.
I've tried....
.Read(read => read.Action("Details", "Practitioner", p => p.practitioner.drkey ))
and combinations of, but I just can't seem to get the right route value in there.
How do I correctly specify the right route values ?
I ended by solving this with a Template.
Not exactly the outcome I was looking for, but close enough at this stage.
columns.Bound(p => p.person.pkey).Template(#<text>
#Html.ActionLink("Details" , "Details" , new { id = #item.person.pkey })
</text>);
The trick is that a traditional MVC razor action is placed inside a Telerik Template.
It also removed the need for the .Select and .Read rows.
When I set the datasource to WebAPI binding, the HTTP GET is being invoked on the controller and it returns the correct data. The problem is that my Kendo Grid is not binding the data correctly, instead I get an empty grid as a result.
#(Html.Kendo().Grid(Model)
.Name("Accounts")
.Columns(columns =>
{
columns.Bound(c => c.Description).Title("Account Name");
columns.ForeignKey(c => c.Type, (System.Collections.IEnumerable)ViewData["accountTypes"], "Id", "Name").Title("Account Type");
columns.ForeignKey(c => c.Currency, (System.Collections.IEnumerable)ViewData["currencyTypes"], "Id", "Name").Title("Account Currency");
columns.Command(command => { command.Edit(); command.Destroy(); }).Width(210);
})
.Sortable(sortable =>
{
sortable.SortMode(GridSortMode.SingleColumn);
sortable.AllowUnsort(false);
})
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5)
)
.ToolBar(toolbar => { toolbar.Create(); })
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.WebApi()
.Model(model =>
{
model.Id(p => p.Id);
model.Field(p => p.Currency).DefaultValue(0).Editable(true);
model.Field(p => p.Description).Editable(true);
model.Field(p => p.Type).Editable(true);
})
.ServerOperation(true)
.Read(read => read.Action("Get", "Accounts"))
.Create(create => create.Action("Post", "Accounts"))
.Update(update => update.Action("Put", "Accounts", new { id = "{0}" }))
.Destroy(destroy => destroy.Action("Delete", "Accounts", new { id = "{0}" }))
.PageSize(10)
)
)
Controller
// GET: api/values
[HttpGet]
public DataSourceResult Get([DataSourceRequest]DataSourceRequest request)
{
return _context.Accounts.ToDataSourceResult(request);
}
I always get an HTTP 200 OK as a result of the paging or sorting command, but the grid is empty afterwards. The URL generated by Kendo is:
http://localhost:58829/Accounts/Get?sort=&page=1&pageSize=10&group=&filter=
Which actually returns JSON when I open it with a browser.
The problem seems to be that you are mixing two different ways to load the data. On the one hand you are passing the Model by param to the grid (this approach is used when ServerOperation = false) and on the other hand you are setting ServerOperation = true and specifying a read action.
The reason why the grid is empty in this case is probably because either the Model is empty.
Take a look at this demo example for a reference on how to implement the remote source databinding: http://demos.telerik.com/aspnet-core/grid/remote-data-binding
Example View:
Example controller:
Hope this helps KendoGrid is an awesome library but like many other libraries it could definitely use better exception handling and more user friendly exceptions :) In my opinion the grid should have shown an exception in this case.
The problem was this: when declaring the Kendo Grid and passing the Model as a parameter like so:
#(Html.Kendo().Grid(Model)
You need to remove the .Read() action, and make sure to use .ServerOperation(false). This works either with WebApi binding or Ajax binding:
.DataSource(dataSource => dataSource
.WebApi() // also works with .Ajax()
.Model(model =>
{
model.Id(p => p.Id);
}
)
.ServerOperation(false)
.Create(create => create.Action("Post", "Invoices"))
.Update(update => update.Action("Put", "Invoices", new { id = "{0}" }))
.Destroy(destroy => destroy.Action("Delete", "Invoices", new { id = "{0}" }))
.PageSize(10)
)
Also, the DataSourceResult Get() method can be removed.
My project has two main issues that prevent me from being able to do a normal, basic Kendo UI gridbind. 1. I do not have full access to the required SQL data (I cannot create a view) 2. One of the fields is incredibly large (the field overloads a ViewModel).
I was able to get my grid to work, but I feel that there is a better way to accomplish the task. My problem, essentially, is that I can only seem to gridbind to a single table of DB. To get passed this, I bound to the foreign key field and used a clientTemplate to fill the data from the read, but I feel like there should be a more direct way to be able to bind the field to the data. My read data is an actionresult Linq join from EF, converted to JSON.
Here is my code, if anyone can make a suggestion. D_Type from table 2 is what I am trying to bind directly to the column:
#(Html.Kendo().Grid<ProjectName.Models.Table1>()
.Name("grid")
.Scrollable()
.ColumnMenu()
.Reorderable(reorder => reorder.Columns(true))
.Columns(columns =>
{
columns.Bound(c => c.P_Name);
columns.Bound(c => c.E_Name);
//Cannot be directly bound to a column because it is part of Table2 table
columns.Bound(c => c.TypeId).Title("Type").ClientTemplate("#= D_Type #").Filterable(false);
columns.Command(command => command.Custom("Details").Click("showDetails")).Title("Results").Width(80);
columns.Bound(c => c.Destination);
columns.Bound(c => c.Location);
columns.Bound(c => c.Version);
columns.Bound(c => c.StartDt);
columns.Bound(c => c.EndDt);
columns.Bound(c => c.StartDt).Title("Duration").ClientTemplate("#: calcDuration(StartDt, EndDt) #").Sortable(false).Filterable(false);
columns.Bound(c => c.);
columns.Bound(c => c.MiscNotes).ClientTemplate("#: errorDisplay(ResultObject) # ").Sortable(false).Filterable(false);
})
//Style dictates full grid height
.HtmlAttributes(new { style = "height: 725px;" })
.Filterable()
.Resizable(resize => resize.Columns(true))
.Sortable(sortable => sortable
.AllowUnsort(true)
.SortMode(GridSortMode.SingleColumn))
.Pageable(pageable => pageable
.Refresh(false)
.PageSizes(true)
.ButtonCount(5))
.Events(e => e.DataBound("onDataBound"))
.DataSource(dataSource => dataSource
.Ajax()
.Sort(sort => sort.Add("StartDt").Descending())
.Read(read => read.Action("Project_Read", "Grid"))
.PageSize(20)
.ServerOperation(true)
)
)
Don't bind your grid to ProjectName.Models.Table1.
Instead, bind it to a ViewModel that contains the fields from Table1 and the field from Table2 that you want to display in the Grid, i.e.
public class SpecificGridViewModel
{
public string P_Name { get; set; }
// Other fields you want from Table1
// ...
// PLUS the field(s) you want from the other, joined table.
public string D_Type { get; set; }
}
Then you have your Project_Read method return a list of these ViewModels that you have constructed from your LINQ query instead of a list of Table1s.
i.e. write a LINQ query that returns the exact data you want(data from Table1 joined with Table2.D_Type via Table1.TypeID) and return a result set of custom ViewModel that you create to hold the data from the query and then bind your grid to that model instead of the "raw" Table1 model.
I'm binding a kendo grid with 12 Columns (12 months), i want a last column that will be the aggregation of all the 12 months (total of the year)..
i have this:
#(Html.Kendo().Grid<WebAnalise.Models.map_sel_fabr_prod>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.nameFabr).Visible(true).Width(50).Title("Fabr");
columns.Bound(p => p.nameProd).Width(100).Title("Prod");
columns.Bound(p => p.tot01).Width(30).Title("Jan");
columns.Bound(p => p.tot02).Width(30).Title("Fev");
columns.Bound(p => p.tot03).Width(30).Title("Mar");
columns.Bound(p => p.tot04).Width(30).Title("Abr");
columns.Bound(p => p.tot05).Width(30).Title("Mai");
columns.Bound(p => p.tot06).Width(30).Title("Jun");
columns.Bound(p => p.tot07).Width(30).Title("Jul");
columns.Bound(p => p.tot08).Width(30).Title("Ago");
columns.Bound(p => p.tot09).Width(30).Title("Set");
columns.Bound(p => p.tot10).Width(30).Title("Out");
columns.Bound(p => p.tot11).Width(30).Title("Nov");
columns.Bound(p => p.tot12).Width(30).Title("Dez");
//I want to add the new column here! Something like this, but aggregation! (tot01 + tot02 + tot03 ... + tot12)!! not only value from one column:
columns.Bound(p => p.tot01).Width(30).Title("TOT");
})
Can anyone help?
Try this
First Make sure your model columns are decimal
Add the Total Column in the end of the grid if you want to use the footer then add client footer.
Add Aggregate as shown
Add Javascript
Finally total column will sum total of given columns dynamically and will show grand total in footer as well.
**************Grid********
#(Html.Kendo().Grid<WebAnalise.Models.map_sel_fabr_prod>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.tot11).Width(30).Title("Nov");
columns.Bound(p => p.tot12).Width(30).Title("Dez");
columns.Bound(c => c.Total).Title("Total")
.ClientTemplate("#= kendo.format('{0:c}',Total) #")
.ClientFooterTemplate("<div>Grand Total: #= kendo.format('{0:c}',sum) #</div>");
}
.DataSource(dataSource => dataSource
.Ajax()
.Aggregates(aggregates =>
{
aggregates.Add(p => p.Total).Sum();
})
.PageSize(20)
.Events(events => events.Error("error_handler"))
.ServerOperation(false)
.Events(e=>e.Edit("onEdit").Save("onSave"))
)
*********Javascript*******************
function onEdit(e)
{
var indexCell = e.container.context.cellIndex;
if (typeof indexCell != "undefined") {
var input = e.container.find(".k-input");
input.blur(function () {
e.model.set("Total", (e.model.tot01 * e.model.tot02 *e.model.tot03);
});
var texbox = e.container.find(".text-box");
texbox.change(function () {
e.model.set("Total", (e.model.tot01 * e.model.tot02 *e.model.tot03);
});
}
}
function onSave(e)
{
//update the aggregate columns
var dataSource = this.dataSource;
e.model.one("change", function () {
dataSource.one("change", function () {
dataSource.aggregates().Total.sum;
});
dataSource.fetch();
});
}
Regards
Shaz
You can use the built-in aggregate functionality of the Kendo UI Grid as shown in this demo:
http://demos.telerik.com/kendo-ui/web/grid/aggregates.html
You can display the aggregate information in the footer template of the last column (shown in the demo)
Are you using LinQ or ADO for data access??? Whichever it is it doesn't really matter but you can just return the sum using your LinQ query or in your stored procedure and tie the sum to a property of your model class
I create the grid like so:
Html.Telerik().Grid<myapp.Date>("dates")
.Name("MyGrid")
.Pageable(paging => paging.PageSize(10))
.Sortable()
.Filterable()
.Groupable()
.DataKeys(keys => keys.Add(c => c.id))
.DataBinding(dataBinding =>
{
dataBinding.Ajax()
.Select("_SelectAjaxEditing", "Dates")
.Insert("_InsertAjaxEditing", "Dates")
.Update("_SaveAjaxEditing", "Dates")
.Delete("_DeleteAjaxEditing", "Dates");
})
.ToolBar(commands => commands.Insert())
.Columns(columns =>
{
columns.Bound(o => o.name);
columns.Bound(o => o.date1);
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Text);
commands.Delete().ButtonType(GridButtonType.Text);
});
})
.Editable(editing => editing.Mode(GridEditMode.InLine))
.Render();
%>
and it runs fine, but if I change the GridEditMode to be anything other than InLine I get the following error:
The model item passed into the dictionary is of type 'myapp.Date', but
this dictionary requires a model item of type
'System.Nullable`1[System.DateTime]'.
There is a system.DateTime field in the grid, is that the reason for this error?
I suppose this is because the date you are passing as model is not nullable, while the date required to model is nullable, try this after changing the model type to "DATETIME?".
Note: If you are declaring property declare it as DateTime? as your field is nullable.
Thanks
Regards,
Dhaval Shukla