MVC viewmodel array not being populated - asp.net-mvc

I have an action which takes a viewmodel.
[HttpPost]
public JsonResult SearchAjax(JQueryDataTablesModel jQueryDataTablesModel, BloodSearchAjaxViewModel searchModel)
{
...
In that viewmodel there is an array
public ReadOnlyCollection<string> mDataProp_ { get; set; }
When I call the action I verify via fiddler that the array data is being passed
However, the array (as well as other arrays in the viewmodel) are null.
Also, if I put in the viewmodel a field calledmDataProp_0 it gets populated.
Updated based on comment. Here is the code from the view where the data is posted. I'm using a jQueryDataTable. I didn't think this code mattered since I verified that the data is in the http request.
/* Initialize table */
var oTable = $('#search-results-table').dataTableWithFilter({
"sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
"sPaginationType": "bootstrap",
"bProcessing": true,
"bServerSide": true,
"sAjaxSource": 'SearchAjax',
"sServerMethod": "POST",
"aoColumns": [
{ "mDataProp": "BloodIdentificationNumber" },
{ "mDataProp": "Status" },
{ "mDataProp": "ExpirationDate" },
{ "mDataProp": "CompanyName" },
{ "mDataProp": "Location" },
{ "mDataProp": "City" }
],
// Initialize our custom filtering buttons and the container that the inputs live in
filterOptions: { searchButton: "search-button", clearSearchButton: "clear-search-button", searchContainer: "search-block" }
});
Any ideas? Thanks!

The default model binder is pretty picky. I think what you're seeing here is simply that the data isn't conforming to the conventions the binder expects. Your options are either 1) massage the data to what the default binder accepts or 2) write a custom model binder. Below are some links for both approaches.
ASP.NET MVC - Custom model binder able to process arrays
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

I found the solution. Studying the project I saw that you must include a line in the Global.asax file.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
// This line
ModelBinders.Binders.Add(typeof(JQueryDataTablesModel), new JQueryDataTablesModelBinder());
}
Where JQueryDataTablesModel use as parameter the line below should be included in the Global.asax and the problem is solved.

Related

Breeze createEntity - give initialValues to complexType

I work in durandal project and use breeze entities.
In my project, I need to create client entity, and on the creating, give initialValues.
Normally, whant you want to give initialValues to new entity, you pass it to createEntity function.
For example:
dataContext.createEntity('employee', {age:40, city:'OurCity'});
So you get new empty instance of employee with default data for age and city.
I want to do it with entity type that contain complexFields.
But it doesn't work.
My entity is client-entity.
Here is the code:
addFormType(store);
function addFormType(store) {
store.addEntityType({
shortName: "FormDTO",
autoGeneratedKeyType: AutoGeneratedKeyType.Identity,
dataProperties: {
key: {//auto primary key. numeratorA and Code cannot be key, becose for new records thier aren't unique
dataType: DataType.Int32, isNullable: false, isPartOfKey: true
}
TaxYear: {
dataType: DataType.String, validators: [Validator.required({ message: 'דרוש' })]
},
Unit_A: {
name: "FormUnit_A",
complexTypeName: 'FormUnit_A:#'
}
}
});
store.registerEntityTypeCtor("FormDTO", null, FormInit);
}
function FormInit(entity) {
validationHelper.initializer(entity);
}
addFormUnit_AType(store);
function addFormUnit_AType(store) {
store.addEntityType({
shortName: "FormUnit_A",
isComplexType: true,
dataProperties: {
CompanyName: {
dataType: DataType.String
},
CompanyAddress: {
dataType: DataType.String
}
}
});
store.registerEntityTypeCtor("FormUnit_A", null, null);
}
I tried to initial it by the follwing rows:
var defaultData = {
TaxYear:0,
Unit_A:{
CompanyName:'ourCompany',
CompanyAddress:'Zar 200 OurCity'
}
};
clientManager.createEntity('FormDTO', defaultData);
But it throws exception: "Object doesn't support property or method 'getProperty'"
I tried also to pass an One-layer object with all of the properties:
var defaultData = {
TaxYear:0,
CompanyName:'ourCompany',
CompanyAddress:'Zar 200 OurCity'
};
clientManager.createEntity('FormDTO', defaultData);
But it throws exception:Object doesn't support property or method 'CompanyName'
So what is the correct way to create-entity with initialValues whan entity contain complex type?
Based of what is working in your project it sounds like you are using something like a camelCase strategy for naming your client side properties. If that is not the case please excuse this answer.
If that is the case then why aren't you using camelCased properties when creating your complex types? Seems simple enough -
var defaultData = {
taxYear:0,
unit_A:{
companyName:'ourCompany',
companyAddress:'Zar 200 OurCity'
}
};
clientManager.createEntity('FormDTO', defaultData);

Posting json array with strings to Asp.Net MVC action controller is always null

I cannot get Asp.Net MVC to bind a list of strings sent as an json object unless I wrap them in a class. I have a hard time believing that it is a must to wrap the list like that. Been looking at various examples and what not for a few hours, but cannot get any other solution to work. What am I missing? Working and non-working code below.
$.ajax({
type: "POST",
traditional: true,
url: "keyapi/activate.json",
data: JSON.stringify({ "keys": [ "1", "2" ] }),
contentType: "application/json; charset=utf-8",
success: function (data) {
alert(data);
}
});
When using an action controller such as the one below "keys" will be null.
[HttpPost]
public Response<List<string>> ActivateKeys(List<string> keys)
{
return KeyService.ActivateKeys(test.Keys);
}
If I add a class to wrap the list it works. Am I forced to do so, or am I missing something here? The code below works, using the same jquery post code.
[HttpPost]
public Response<List<string>> ActivateKeys(Test test)
{
return KeyService.ActivateKeys(test.Keys);
}
public class Test
{
List<string> Keys { get; set; }
}
I'd suggest two things
1) Replace
data: JSON.stringify({ "keys": [ "1", "2" ] }),
by
data: JSON.stringify([ "1", "2" ] ), // <-- Send the array directly
doing this you'll be passing an array as parameter instead of an object containing an array propery named keys
2) If previous suggestion alone doesn't work, replace
ActivateKeys(List<string> keys)
by
ActivateKeys(IList<string> keys) // <-- Use interface IList<T>
as not 100% sure if mvc engine would be able to resolve to List<string>

Where can i find an example of jqGrid being used as part of form for posting?

I have a form where I have a bunch of textbox and dropdowns. I now need to add another array of sub objects and include that as part of the my form post.
I was going to hand roll this as an html table but i thought that i could leverage jqGrid. What is the best way I can use jqGrid locally to add data and then have that included in the form post? The reason that i need jqGrid to act locally is that these are subrecords as part of the larger form so I can't post the jqGrid rows until the larger form is posted (so i have an Id to join these rows with)
So for example, if my post was an Order screen (with textboxes for date, instructions, etc) and now i want to have a grid that you can add products into the order. You can have as many rows as you want . .)
my backend is asp.net-mvc if that helps with any suggestions.
If you use form editing you can extend the postdata in many ways. The most simple one is the usage of onclickSubmit callback:
onclickSubmit: function (options, postData) {
return {foo: "bar"};
}
If you use the above callback then the data which will be post to the server will be extended with the parameter foo with the string value "bar".
Another possibility is the usage of editData option of editGridRow. The best way is to use properties of editData defined as function. In the way the funcion will be called every time before posting of data.
For example the following code
$("#grid").jqGrid("navGrid", "#pager", {}, {
editData: {
foo: function () {
return "bar";
}
},
onclickSubmit: function (options, postData) {
return {test: 123};
}
});
will add foo=bar and test=123 to the parameters which will be send to the server.
The next possibility will be to use serializeEditData. The callback gives you full control on the data which will be sent to the server.
I am using the method of serialization as Oleg suggested.
view
$( "#Save" ).click( function ( e )
{
e.preventDefault();
var griddata= $( "#list" ).getRowData();
var model = {
grid: griddata
};
var gridVal = JSON.stringify( model );
//Now set this value to a hiddenfield in the form and submit
$('#hiddenGridDta').val(gridVal );
$( 'form' ).submit();
});
And in the controller, deserialize the values using Newtonsoft.json.jsonconvert().
public ActionResult SaveTest(TestClass test)
{
testViewModel myGrid = JsonConvert.DeserializeObject<testViewModel>(test.hiddenGridDta);
................
}
testViewModel class
public class testViewModel
{
public IEnumerable<TestGrid> grid { get; set; }
}
TestGrid class
public class profileGrid
{
//fields in the jqgrid (should use the same name as used in *colModel:* of jqgrid)
public int x
{
get;
set;
}
public int y
{
get;
set;
}
.......
}

Kendo, how to do Grid server paging using mvc4 helper

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);

How to send a model in jQuery $.ajax() post request to MVC controller method

In doing an auto-refresh using the following code, I assumed that when I do a post, the model will automatically sent to the controller:
$.ajax({
url: '<%=Url.Action("ModelPage")%>',
type: "POST",
//data: ??????
success: function(result) {
$("div#updatePane").html(result);
},
complete: function() {
$('form').onsubmit({ preventDefault: function() { } });
}
});
Every time there is a post, I need to increment the value attribute in the model:
public ActionResult Modelpage(MyModel model)
{
model.value = model.value + 1;
return PartialView("ModelPartialView", this.ViewData);
}
But the model is not passed to the controller when the page is posted with jQuery AJAX request. How can I send the model in the AJAX request?
The simple answer (in MVC 3 onwards, maybe even 2) is you don't have to do anything special.
As long as your JSON parameters match the model, MVC is smart enough to construct a new object from the parameters you give it. The parameters that aren't there are just defaulted.
For example, the Javascript:
var values =
{
"Name": "Chris",
"Color": "Green"
}
$.post("#Url.Action("Update")",values,function(data)
{
// do stuff;
});
The model:
public class UserModel
{
public string Name { get;set; }
public string Color { get;set; }
public IEnumerable<string> Contacts { get;set; }
}
The controller:
public ActionResult Update(UserModel model)
{
// do something with the model
return Json(new { success = true });
}
If you need to send the FULL model to the controller, you first need the model to be available to your javascript code.
In our app, we do this with an extension method:
public static class JsonExtensions
{
public static string ToJson(this Object obj)
{
return new JavaScriptSerializer().Serialize(obj);
}
}
On the view, we use it to render the model:
<script type="javascript">
var model = <%= Model.ToJson() %>
</script>
You can then pass the model variable into your $.ajax call.
I have an MVC page that submits JSON of selected values from a group of radio buttons.
I use:
var dataArray = $.makeArray($("input[type=radio]").serializeArray());
To make an array of their names and values. Then I convert it to JSON with:
var json = $.toJSON(dataArray)
and then post it with jQuery's ajax() to the MVC controller
$.ajax({
url: "/Rounding.aspx/Round/" + $("#OfferId").val(),
type: 'POST',
dataType: 'html',
data: json,
contentType: 'application/json; charset=utf-8',
beforeSend: doSubmitBeforeSend,
complete: doSubmitComplete,
success: doSubmitSuccess});
Which sends the data across as native JSON data.
You can then capture the response stream and de-serialize it into the native C#/VB.net object and manipulate it in your controller.
To automate this process in a lovely, low maintenance way, I advise reading this entry that spells out most of native, automatic JSON de-serialization quite well.
Match your JSON object to match your model and the linked process below should automatically deserialize the data into your controller. It's works wonderfully for me.
Article on MVC JSON deserialization
This can be done by building a javascript object to match your mvc model. The names of the javascript properties have to match exactly to the mvc model or else the autobind won't happen on the post. Once you have your model on the server side you can then manipulate it and store the data to the database.
I am achieving this either by a double click event on a grid row or click event on a button of some sort.
#model TestProject.Models.TestModel
<script>
function testButton_Click(){
var javaModel ={
ModelId: '#Model.TestId',
CreatedDate: '#Model.CreatedDate.ToShortDateString()',
TestDescription: '#Model.TestDescription',
//Here I am using a Kendo editor and I want to bind the text value to my javascript
//object. This may be different for you depending on what controls you use.
TestStatus: ($('#StatusTextBox'))[0].value,
TestType: '#Model.TestType'
}
//Now I did for some reason have some trouble passing the ENUM id of a Kendo ComboBox
//selected value. This puzzled me due to the conversion to Json object in the Ajax call.
//By parsing the Type to an int this worked.
javaModel.TestType = parseInt(javaModel.TestType);
$.ajax({
//This is where you want to post to.
url:'#Url.Action("TestModelUpdate","TestController")',
async:true,
type:"POST",
contentType: 'application/json',
dataType:"json",
data: JSON.stringify(javaModel)
});
}
</script>
//This is your controller action on the server, and it will autobind your values
//to the newTestModel on post.
[HttpPost]
public ActionResult TestModelUpdate(TestModel newTestModel)
{
TestModel.UpdateTestModel(newTestModel);
return //do some return action;
}
I think you need to explicitly pass the data attribute. One way to do this is to use the
data = $('#your-form-id').serialize();
This post may be helpful.
Post with jquery and ajax
Have a look at the doc here..
Ajax serialize
you can create a variable and send to ajax.
var m = { "Value": #Model.Value }
$.ajax({
url: '<%=Url.Action("ModelPage")%>',
type: "POST",
data: m,
success: function(result) {
$("div#updatePane").html(result);
},
complete: function() {
$('form').onsubmit({ preventDefault: function() { } });
}
});
All of model's field must bo ceated in m.
In ajax call mention-
data:MakeModel(),
use the below function to bind data to model
function MakeModel() {
var MyModel = {};
MyModel.value = $('#input element id').val() or your value;
return JSON.stringify(MyModel);
}
Attach [HttpPost] attribute to your controller action
on POST this data will get available

Resources