I am using a WebApi controller to return an IDictionary to jQuery autocomplete like so:
public IDictionary<int, string> GetClientAuto(string term)
{
var clients = db.Clients.Where(n => n.Name.Contains(term)).OrderBy(n => n.Name);
return clients.ToDictionary(n => n.ClientID, n => n.Name);
}
The issue is although I add a breakpoint and check the variable clients is sorting by Name turns to to be true, the order shown in the autocomplete box is different, possibly I expect by the ID. I tried adding this to the autocomplete: sortResults:false, but with no effect.
I have this in my succes function, is there something here maybe I need to change for the order to work on the label i.e. Name:
success: function (json) {
// call autocomplete callback method with results
response($.map(json, function (name, val) {
return {
label: name,
value: val
}
}));
},
Its being sorted by the ToDictionary call, the order of dictionaries isn't actually defined (http://msdn.microsoft.com/en-us/library/yt2fy5zk.aspx) as it shouldn't matter, however i believe it is typically the value of the key, not the value.
You could resort it in javascript to be by name, or you could return something other than a dictionary from your api. Either a IEnumerable> or IEnumerable would do the trick.
Alterntively look into the OrderBy methods on the dictionary, however they all appear to return a list of KeyValuePairs.
End result should look something like
public IEnumerable<KeyValuePair<int, string>> GetClientAuto(string term)
{
return db.Clients.Where(n => n.Name.Contains(term)).OrderBy(n => n.Name).Select(n => new KeyValuePair<int, string>(n.ClientID, n.Name ));
}
success: function (json)
{
response($.map(json, function (item) {
return {
label: item.Value,
value: item.Key
}
}));
},
Related
I am trying to populate a telerik grid from a table based on date selected.
I get an error message:
System.InvalidOperationException: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property
Is there any other way I can populate Telerik grids on client events?
view:
<%= Html.Telerik().DatePicker().Min("01/01/2011")
.Name("Date")
.ClientEvents(e => e.OnChange("Date_onChange"))
%>
<div>
<%= Html.Telerik().Grid<DataAccess.Entity.Dto.ReportData>()
.Name("tblGrid")
.DataBinding(databinding => databinding.Ajax()
.Select("MasterDataGrid", "Report"))
.Columns(col =>
{
col.Bound(f => f.ID).Title("ID Number");
col.Bound(f => f.Date).Title("Date");
})
.Filterable(filter => filter.Enabled(true))
.Pageable(page => page.PageSize(200))
.Sortable(sort => sort.Enabled(true))
%>
</div>
JavaScript:
function Date_onChange() {
var link = '/Report/DataGrid';
var Date = $("#Date").val().toString();
$.ajax({
type: 'POST',
url: link,
data: { date: Date },
dataType: 'json',
success: function (result) {
$("#tblGrid").data("tGrid").dataBind(result.data);
},
error: function (result) {
$("#ErrorMessage").text(result.message);
}
});
};
Controller:
[HttpPost]
[GridAction]
public ActionResult DataGrid(DateTime date)
{
var model = context.GetReportData_tblData(date);
return View(new GridModel { Data = model });
}
It looks like you are returning far too much data. There are two possible scenarios here, and it may be one or the other or both.
(1) As your page size is 200, this implies that you are expecting a lot of data, so you may be returning far too many objects. By default, the Telerik Grid will take the entire collection and do the sorting and filtering client-side. I would try implementing server-side paging and sorting so that you only return 1 page of data at a time.
View:
#(Html.Telerik.Grid<IEnumerable<MyClass>>()
.Name("MyGrid")
.Columns(col => // column definitions)
.EnableCustomBinding(true)
.Pageable(pg =>
{
paging.Total(Model.TotalCount);
paging.PageSize(Model.PAGE_SIZE);
})
.DataBinding(db =>
{
db.Ajax().Select("DataGrid", "MyController");
}
)
Controller:
[GridAction(EnableCustomBinding = true)]
public ActionResult DataGrid(GridCommand command, DateTime date)
{
MyModel model = new MyModel();
IEnumerable<MyClass> data = context.GetReportData_tblData(date);
// apply paging, filtering, grouping, and sorting
GridModel gm = data.AsQueryable().ToGridModel(command.Page, command.PageSize, command.SortDescriptors, command.FilterDescriptors, command.GroupDescriptors);
return View(gm);
}
Telerik provides a handy extension method to apply all the paging, sorting, filtering and grouping: IQueryable<T>.ToGridModel(), which you can find in the Telerik.Web.Mvc.Extensions assembly (h/t: vladimir77).
(2) If your DataAccess.Entity.Dto.ReportData class has a lot of properties, and/or some of its properties have long strings or child objects which are also recursively serialized, then you can easily hit the transfer limit with just a few objects. I notice you are only using two properties in the Grid. Please consider creating view model objects that only have the data that you need (that's good practice anyway).
You can increase the default JavaScriptSerializer.MaxJsonLength. Just see this answer: https://stackoverflow.com/a/7207539/443379
After this the filtering, paging... functionality won't work anymore. All you have to do though is use CustomBinding (inspired from #howcheng's answer):
[GridAction(EnableCustomBinding=true)]
public ActionResult DataGrid(GridCommand command, DateTime date)
{
var data = context.GetReportData_tblData(date)
.ToGridModel(command.Page, command.PageSize, command.SortDescriptors, command.FilterDescriptors, command.GroupDescriptors);
var serializer = new JavaScriptSerializer();
var result = new ContentResult();
serializer.MaxJsonLength = Int32.MaxValue; // overriding the default max length
result.Content = serializer.Serialize(data);
result.ContentType = "application/json";
return result;
}
This works even for Client Operation Mode. You can refactor this to a class: http://macaalay.com/2011/09/27/large-json-result-for-teleriks-mvc-grid/
Here's my issue. I'm making an ajax request to obtain an object from a controller. The object (or something) is being brought back, but I don't know how to access the attributes of that object being brought back. The object is of type "Address" and thus has attributes like Address.Address1, Address.City, etc. Here is my code: After a button is clicked,
function showEditAddress(addressid) {
$.get("/Website/Accommodation/AddressGet",
{ guid: addressid.toString() },
function(data) {
//Get values from variable 'data' such as described above
//and append to form 'dialog'
$("#dialog").dialog({
// autoOpen: false,
show: {
effect: "explode",
duration: 250
},
hide: {
effect: "explode",
duration: 250
},
buttons: {
"Save": {
text: "Save",
class: "",
click: function () {
//save form
$(this).dialog("close");
}
},
"Cancel": {
text: "Cancel",
class: "",
click: function () {
$(this).dialog("close");
}
}
},
modal: true
});
});
}
Controller action:
public Address AddressGet(string guid)
{
Guid g = new Guid(guid);
return _iCoreRepository.Addresses.Where(p => p.AddressID == g).SingleOrDefault();
}
Any help will be greatly appreciated!!! Thank you!!!
Jose is quite simple. You already answer the question. To get the value of the properties of Address you only need put a dot in front of data and type de property name. like this:
//(....)
function(data) {
//Get values from variable 'data' such as described above
//and append to form 'dialog'
//show address id.
alert(data.AddressID);
Remember that javascript is case sensitive, so you need use upper case to A and ID in AddressID like you did in C#.
And in controller you need replace the last line to something like this:
var address = _iCoreRepository.Addresses.Where(p => p.AddressID == g).SingleOrDefault();
return Json(address, JsonRequestBehavior.AllowGet);
the method must be return JsonResult. The method Json serialize the object, in this case address, in a json format before response the data to the client.
If you need the Json method accepts IEnumerables to, like List<> or Array. In javascript your data object will be a lenght property and will acess each element by using indexer like:
data[0].AddressID.
Instead of returning just your Address object you can try returning a JsonResult.
public ActionResult AddressGet(string guid)
{
Guid g = new Guid(guid);
var address = _iCoreRepository.Addresses.Where(p => p.AddressID == g).SingleOrDefault();
return Json(address, JsonRequestBehavior.AllowGet);
}
Then in your jquery, the returned data is your address object and you can access the fields as they appear in your C# class:
function showEditAddress(addressid) {
$.get("/Website/Accommodation/AddressGet",
{ guid: addressid.toString() },
function(data) {
//Get values from variable 'data' such as described above
//and append to form 'dialog'
//Access it like: data.Address1, data.City etc...
});
}
}
I need help writing the jquery/ajax to fill a Select2 dropdown box.
For those who don't know what Select2 is, it is a javascript extension to provide Twitter Bootstrap looks and search / type-ahead functionality to an html select list dropdown box. For more information look at the examples here: Select2 Github page
UPDATED - Solved!
So I finally put this all together, and the solution to my problems was that I was missing functions to format the results and the list selection. The code below produces a functioning Select2 dropbox with type-ahead perfectly.
Json Method on Controller:
public JsonResult FetchItems(string query)
{
DatabaseContext dbContext = new DatabaseContext(); //init dbContext
List<Item> itemsList = dbContext.Items.ToList(); //fetch list of items from db table
List<Item> resultsList = new List<Item>; //create empty results list
foreach(var item in itemsList)
{
//if any item contains the query string
if (item.ItemName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
{
resultsList.Add(item); //then add item to the results list
}
}
resultsList.Sort(delegate(Item c1, Item c2) { return c1.ItemName.CompareTo(c2.ItemName); }); //sort the results list alphabetically by ItemName
var serialisedJson = from result in resultsList //serialise the results list into json
select new
{
name = result.ItemName, //each json object will have
id = result.ItemID //these two variables [name, id]
};
return Json(serialisedJson , JsonRequestBehavior.AllowGet); //return the serialised results list
}
The Json controller method above returns a list of serialised Json objects, whose ItemName contains the string 'query' provided (this 'query' comes from the search box in the Select2 drop box).
The code below is the Javascript in the view(or layout if you prefer) to power the Select2 drop box.
Javascript:
$("#hiddenHtmlInput").select2({
initSelection: function (element, callback) {
var elementText = "#ViewBag.currentItemName";
callback({ "name": elementText });
},
placeholder: "Select an Item",
allowClear: true,
style: "display: inline-block",
minimumInputLength: 2, //you can specify a min. query length to return results
ajax:{
cache: false,
dataType: "json",
type: "GET",
url: "#Url.Action("JsonControllerMethod", "ControllerName")",
data: function (searchTerm) {
return { query: searchTerm };
},
results: function (data) {
return {results: data};
}
},
formatResult: itemFormatResult,
formatSelection: function(item){
return item.name;
}
escapeMarkup: function (m) { return m; }
});
Then in the body of the view you need a hidden Input element, which Select2 will render the dropbox to.
Html:
<input id="hiddenHtmlInput" type="hidden" class="bigdrop" style="width: 30%" value=""/>
Or attach a MVC Razor html.hidden element to your view model to enable you to post the picked item Id back to the server.
Html (MVC Razor):
#Html.HiddenFor(m => m.ItemModel.ItemId, new { id = "hiddenHtmlInput", #class = "bigdrop", style = "width: 30%", placeholder = "Select an Item" })
Solved! Finally.
The full jquery is below, what was needed were two functions to format the returned results from the controller. This is because the dropbox needs some html markup to be wrapped around the results in order to be able to display them.
Also contractID was needed as an attribute in the controller as without it results were shown in the dropdown, but they could not be selected.
$("#contractName").select2({
placeholder: "Type to find a Contract",
allowClear: true,
minimumInputLength: 2,
ajax: {
cache: false,
dataType: "json",
type: "GET",
url: "#Url.Action("FetchContracts", "Leads")",
data: function(searchTerm){
return { query: searchTerm };
},
results: function(data){
return { results: data };
}
},
formatResult: contractFormatResult,
formatSelection: contractFormatSelection,
escapeMarkup: function (m) { return m; }
});
function contractFormatResult(contract) {
var markup = "<table class='contract-result'><tr>";
if (contract.name !== undefined) {
markup += "<div class='contract-name'>" + contract.name + "</div>";
}
markup += "</td></tr></table>"
return markup;
}
function contractFormatSelection(contract) {
return contract.name;
}
The problem is that you are returning a List<Contract> from that controller method, but the MVC runtime doesn't know how to hand that off to the browser. You need to return a JsonResult instead:
public JsonResult FetchContracts()
{
TelemarketingContext teleContext = new TelemarketingContext();
var contracts = teleContext.Contracts.ToList();
var json = from contract in contracts
select new {
name = contract.ContractName,
id = contract.ContactID,
};
return Json(json, JsonRequestBehavior.AllowGet);
}
Now, the data param of the AJAX : Success function will be the JSON from the controller. I'm not familiar with how this plugin works, but you should be able to loop through the json in data manually if you need to.
Select 2 seems to be a standard select with jquery attached so this should work:
Model:
public class vmDropDown
{
public IEnumerable<SelectListItem> DeviceList { get; set; }
[Required(ErrorMessage = "Please select at least one item")]
public IEnumerable<int> SelectedItems { get; set; }
}
Controller:
[HttpGet]
public ActionResult Assign(int id)
{
return View(CreateUnassignedModel(id));
}
[HttpPost]
public ActionResult Assign(vmDeviceAssign model)
{
if (ModelState.IsValid)
{
_deviceLogic.Assign(model.GroupId, model.SelectedItems);
return View("ConfirmDevice");
}
else // Validation error, so redisplay data entry form
{
return View(CreateUnassignedModel(model.GroupId));
}
}
private vmDeviceAssign CreateUnassignedModel(int id)
{
return new vmDeviceAssign
{
DeviceList = _deviceLogic.GetUnassigned(),
SelectedItems = null
};
}
View:
<div class="editor-field">
#Html.ListBoxFor(model => model.SelectedItems, new SelectList(Model.DeviceList, "Value", "Text"))
#Html.ValidationMessageFor(model => model.SelectedItems)
</div>
Cant give explanation as am at work but if you leave a message ill pick it up tonight
I am using mvc4. On one of my page, it has Kendo Grid. I want to show 5 rows per page. I have no problem doing it using pure javascript, however, If I am using mvc helper. I got lost, couldn't find any samples online.
here's my javascript code.
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#grid").kendoGrid({
dataSource: {
type: "json",
serverPaging: true,
pageSize: 5,
transport: { read: { url: "Products/GetAll", dataType: "json"} },
schema: { data: "Products", total: "TotalCount" }
},
height: 400,
pageable: true,
columns: [
{ field: "ProductId", title: "ProductId" },
{ field: "ProductType", title: "ProductType" },
{ field: "Name", title: "Name" },
{ field: "Created", title: "Created" }
],
dataBound: function () {
this.expandRow(this.tbody.find("tr.k-master-row").first());
}
});
});
now if i am using mvc helper
#(Html.Kendo().Grid(Model)
.Name("Grid") //please help me to finish the rest
Update:
Adding the action code.
[HttpPost]
public ActionResult GetAll([DataSourceRequest]DataSourceRequest request, int id)
{
var products= ProductService.GetAll(id);
return Json(products.ToDataSourceResult(request));
}
GetAll method in the service layer:
public IQueryable<Product> GetAll(int id)
{
var products= ProductRepository.Get(p => p.Id== id && p.IsActive == true, null, "ProductionYear")
.OrderBy(o => o.Name); //.ToList();
return Product.Select(p => new ProductVM()
{
Name = p.Name,
ProductionYear= p.ProductionYear.Year.ToString()
Id = p.Id
}).AsQueryable();
}
now, if I run the app, i will get following error:
"LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression."}
in the GetAll method, I comment out the "ToList()". If I use ToList, everything works. but I will query all the rows back instead just those rows I needed for that page.
You can set the PageSize inside the DataSource method. So you will need something like:
#(Html.Kendo().Grid(Model)
.Name("Grid")
.DataSource(dataSource => dataSource.Ajax()
.PageSize(5)
.Read(c => c.Action("GetAll", "Products")
.Model(s => s.Id(m => m.Id)))
.Columns(columns =>
{
columns.Bound(m => m.ProductId).Title("ProductId");
//other colums
})
.Events(g => g.DataBound("somefunction"))
.Pageable(true))
You can find more info the KendoUI Grid's Asp.NET MVC wrappers documentation.
If you are not using Kendo's ASP.NET MVC wrappers and are using the Kendo JavaScript objects directly and you are trying to do server side paging, then you need to create your datasource as follows:
var dataSource = {
"type":"aspnetmvc-ajax",
"serverSorting": true,
"serverPaging": true,
"page": 1,
"pageSize": 8,
"schema": {
"data":"items",
"total":"count",
"errors":"errors"
}
};
And your Read controller method will look something like:
public ActionResult List_Read([DataSourceRequest]DataSourceRequest request) {
try {
var model = null /* prepare your model object here contain one page of grid data */;
// using Json.NET here to serialize the model to string matching the
// schema expected by the data source
var content = JsonConvert.SerializeObject(new { count = model.Count, items = model.Items }, Formatting.None);
return Content(content, "application/json");
}
catch (Exception exception) {
// log exception if you need to
var content = JsonConvert.SerializeObject(new { errors = exception.Message.ToString() }, Formatting.None);
return Content(content, "application/json");
}
}
type, serverSorting and serverPaging are important to make sure paging and sorting happens server-side. type must be aspnetmvc-ajax, otherwise the query data will not be recognized by the model binder that has been specified by the [DataSourceRequest] attribute in the Read method. You cannot omit that attribute unless you want to write your own custom modelbinder to parse the query data sent by the kendo dataSource, which does not conform to the modelbinding conventions used by the DefaultModelBinder in ASP.NET MVC.
The other answer will work if you are using Kendo UI for ASP.NET MVC. If you don't you need to implement paging yourself. The Kendo DataSource will send pageSize and current page when making a request. You can use that to do the paging. It also sends "take" and "skip" which makes things even easier (hint Linq).
The error
"LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression."}
is self explanatory, to gat around this, you should do something like
return Product.**AsEnumerable**.Select(p => new ProductVM()
{
Name = p.Name,
ProductionYear= p.ProductionYear.Year.ToString()
Id = p.Id });
using AsEnumerable brings all records from the db, so best to use after any filtering
products.where(x => x.active = true).AsEnumerable
rather then
products.AsEnumerable.where(x => x.active = true)
Download kendo examples, then unzip and follow
<your directory>\Kendo UI for ASP.NET MVC Q1 2013\wrappers\aspnetmvc\Examples\Areas\razor\Views\web\grid\
for the view index
and
<your directory>\Kendo UI for ASP.NET MVC Q1 2013\wrappers\aspnetmvc\Examples\Controllers
for IndexController
in your view you might also want .ServerOperation(true) as bellow to avoid
Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
If you have big data to fetch
#(Html.Kendo().Grid(<yourmodel>)
.Name("Grid")
.Columns(columns =>
...
})
.Filterable()
.Pageable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(8)
.ServerOperation(true) )
.Model(model =>
{
model.Id(p => p.Id);
...
})
)
)
also in controller in ActionResult Products_Read consider
DataSourceResult result = GetProducts().ToDataSourceResult(request, o => new { Id = o.Id, ... } );
return Json(result , JsonRequestBehavior.AllowGet);
i am making an application in MVC3 where in i have many dropdowns
Now on the basis of value selected in the first dropdown the second drodown is populated.
I need to write Ajax for this and i am having a tough time doing it.
I Have my first dropdown "Select Course" and now which ever course is selected With the help of that particular Course Id the corresponding state is selected from the database.
For eg if the Course is "MCA" the states Dropdown must be populated with states such as Maharashtra,Rajasthan and soon.
I tried the Following code for Ajax but it does not work and gives me an error
$("#Course").change(function () {
var Courseid = $("#Course").val();
var urlCourselocation = '#Url.Action("FetchstatebyCourse")';
$.ajax({
type: "POST",
url: urlCourselocation,
data: { id: Courseid },
success: function (data) {
$('#State').empty();
$('#State')
.append($('<option>', { value: "0" })
.text("-- Select State --"));
$.each(returndata, function (key, value) {
$('#State')
.append($('<option>', { value: value.Value })
.text(value.Text));
});
}
});
});
and in the Controller i have wrote the following function:
public ActionResult FetchHobbyDetailByHobbyId(int Id)
{
LearnerService learnerservice = new LearnerService();
/* Here is some Code to fetch the state id from the database*/
ICollection<ProvincialState> state = learnerservice.FetchStateByStateid(stateid);
ViewBag.State = state;
if (HttpContext.Request.IsAjaxRequest())
return Json(new SelectList(
state,
"ProvincialStateID",
"ProvincialStateName"));
return View();
}
Please help me with the Code and correct me if i am wrong..
Oh I think I saw the issue...
shouldn't :
$.each(returndata, function (key, value) {
be
$.each(data, function (key, value) {