MVC3 fails to parse JSON - c#

Using the following action from a MVC3 controller:
[HttpPost]
public ActionResult GetAsiguratiSuplimentari(int numarAsiguratiSuplimentari, AcpComplexAsigurat[] oldData)
{
var newData = new List<AcpComplexAsigurat>(oldData);
for(int i = newData.Count; i < numarAsiguratiSuplimentari; i++)
{
newData.Add(new AcpComplexAsigurat());
}
return PartialView("AsiguratiSuplimentari", newData);
}
and the js:
$("#NumarAsiguratiSuplimentari").change(function (e) {
var data = { oldData: $("#div-asigurati-suplimentari").find('input').serialize(), numarAsiguratiSuplimentari: $("#NumarAsiguratiSuplimentari").val() };
var postData = JSON.stringify(data);
$.post('#Url.Action("GetAsiguratiSuplimentari")', postData, function (data) {
$("#div-asigurati-suplimentari").html(data);
});
});
I want to get the select option the user chose and serialize a table of inputs to send to the above action:
<h3>Asigurati</h3>
<div class="field half odd">
<label>Numar asigurati suplimentari</label>
<select id="NumarAsiguratiSuplimentari" name="NumarAsiguratiSuplimentari">
#for (int i = 0; i <= 30; i++)
{
<option value="#i" #(Model.Asigurati.Count == i ? "selected" : "")>#i</option>
}
</select>
</div>
<div class="field full" id="div-asigurati-suplimentari">
#if (Model.Asigurati.Count > 0)
{
<table id="Asigurati">
<thead>
<tr>
<th>CNP</th>
<th>Nume</th>
<th>Prenume</th>
<th>Data nasterii</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Asigurati)
{
<tr>
<td style="padding: 3px;">#Html.TextBoxFor(x => Model.Asigurati[Model.Asigurati.IndexOf(item)].Cnp, new { maxlength = 13 })</td>
<td style="padding: 3px;">#Html.TextBoxFor(x => Model.Asigurati[Model.Asigurati.IndexOf(item)].Nume)</td>
<td style="padding: 3px;">#Html.TextBoxFor(x => Model.Asigurati[Model.Asigurati.IndexOf(item)].Prenume)</td>
<td style="padding: 3px;">#Html.TextBoxFor(x => Model.Asigurati[Model.Asigurati.IndexOf(item)].DataNastere)</td>
</tr>
}
</tbody>
</table>
}
</div>
In monorail, I would have had to decorate the input parameters with a {JsonBinder] attribute, but in MVC3 i am not sure how to proceed.
It basically gives me the following error:
The parameters dictionary contains a null entry for parameter
'numarAsiguratiSuplimentari' of non-nullable type 'System.Int32' for
method 'System.Web.Mvc.ActionResult GetAsiguratiSuplimentari(Int32,
BrokerPlatform.Services.AcpComplex.AcpComplexAsigurat[])' in
'BrokerPlatform.Areas.AcpComplex.Controllers.CarpaticaController'. An
optional parameter must be a reference type, a nullable type, or be
declared as an optional parameter. Parameter name: parameters
Here is the JSON that it sends:
{"numarAsiguratiSuplimentari":"2","oldData":""}

Parameters are inconsistent.
public ActionResult GetAsiguratiSuplimentari(int numarAsiguratiSuplimentari?, AcpComplexAsigurat[] oldData)
int? numarAsiguratiSuplimentari is nullable ?
or
$.post('#Url.Action("GetAsiguratiSuplimentari")', {numarAsiguratiSuplimentari:number,AcpComplexAsigurat:list}, function (data) {
...{numarAsiguratiSuplimentari:number,AcpComplexAsigurat:list}....
You can parse the string and get the json, and convert to model.
My sample;
[HttpPost()]
public ActionResult GetAsiguratiSuplimentari(string model)
{
List<myModel> myModel = JsonConvert.DeserializeObject<List<muModel>>(model);
}

I don't have MVC 3 right now, but this is working fine with MVC 4 (using Fiddler). There are a few things you can try:
Explicitly set dataType: "json" in the $.post call, jQuery might not be able to guess it.
Change the method signature to accept a string instead of an int, the binding in MVC 3 might not be able to do it automatically.
If one of these work, let me know so I can remove the incorrect option.

Related

Update table in foreach loop with filtered model with Ajax (asp.net mvc c#)

I've developed an asp.net MVC web app where I have a table that shows some items in a model.
I can filter it now with a dropdown list using ajax
The model that i pass to the table is correct (if i go to the model before the foreach there are 3 rows instead of 10 thanks to the filter)
The problem is that the table doesn't change, it always shows all the rows as the initial request.
It look like it works but the table won't update...
There's my jquery ajax call:
$("#Dropdown1Id").on('change', function () {
//console.log("onchange");
//console.log($("#Dropdown1Id").val());
var drpdown1 = $("#Dropdown1Id").val();
var submit = $("#submitButton");
$.ajax({ // crea una chiamata AJAX
data: { data: drpdown1 }, // prendi i dati del form in questo caso del primo dropdown
type: "GET", // GET o POST
url: "/Select/Filter", // li passa al controller
success: function () { // se va con successo esegue il codice seguente
submit.click();
$("#frmId").submit();
},
error: function (error) {
console.log("error")
}
});
});
There's my controller action:
public ActionResult Filter(string data)
{
List<Card> cards = new List<Card>();
ViewBag.stato = new SelectList(myApi.GetState(), "Name", "Name");
if (data != null && data != "")
{
foreach (var card in model)
{
if (card.IdList == data || data == "")
cards.Add(card);
}
return View(cards);
}
return View(model);
}
There's my view with the daple and the dropdown:
#using (Html.BeginForm(new { id = "frmId"}))
{
#Html.AntiForgeryToken()
<table id="tb2">
<tr>
<th>
<h4> LIST : #Html.DropDownList("stato", null, new { #id = "Dropdown1Id" })</h4>
</th>
#*<th>
<h4>ARCHVIED : #Html.DropDownList("closed", null, new { #id = "Dropdown2Id" })</h4>
</th>*#
<th>
<input type="submit" value="Filter" class="btn btn-info" id="submitButton" />
</th>
</tr>
</table>
<br />
<div id="risultato"></div>
<table class="table" id="tb1">
<tr>
<th style="text-align:center">
TRELLO'S CARDS LIST
</th>
<th>LIST</th>
<th>ARCHVIED</th>
<th>Expiration date</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.IdList)
</td>
#if (item.Closed == "True")
{
<td>YES</td>
}
else
{
<td>NO</td>
}
#if (item.Due != null)
{
<td>
#Html.DisplayFor(modelItem => item.Due)
</td>
}
else
{
<td>
Not Present
</td>
}
</tr>
idList.Add(item.Id);
}
</table>
Let me get you through the execution stack and you'll understand why:
Your MVC view is loaded. When the view is returned to the frontend it is already in Html format. Check Server side rendering here
Basically it means that #foreach (var item in Model) will only execute on the server side and will not re-run when you hit an ajax call. This will only happen on a full post.
While in your page you fire up change dropdown event and the following happens:
An ajax call hit your controller
Data are being returned to the success function
Your success: function () is being executed.
A new form post occurs. See that you didn't do anything with the return data that was returned in the success: function(). You just posted back to the controller
After the post, the full view has returned ignoring any changes in the dropdown and in the data returned.
There are 2 solutions for your problem:
Do a full post and return a new view with the proper data
Write some more javascript to change the DOM inside your sucess function

Post one item from model collection to controller method in MVC

I have a view where I display a list of users, setting the model as:
#model List<MyProject.Models.User>
In this view I want to be able to choose to perform an action on a specific user, i.e. post to the controller that I want to disable a user. How do I post the specific User object to the controller?
This is what I've got so far, but I can't see how to post the specific object from the collection:
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
My controller has the signature:
public ActionResult DisableUser(User user)
Rather than posting back all propeties of User, you can just add a route value in the BeginForm() method to post back the ID or the User. Assuming that property is namedUserId`, then
#foreach (var c in Model)
{
<tr>
....
<td>
#using (Html.BeginForm("DisableUser", "UserManagement", new { id = c.UserId ))
{
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
and the controller method would be
public ActionResult DisableUser(int id)
{
// Get the User based on id, update it and redirect
}
You could also consider using ajax to submit the value, which would allow the user to stay on the same page and continue to 'disable' other User objects without need to make a redirect, in which case the code might be
#foreach (var c in Model)
{
<tr>
....
<td>
<button type="button" class="disable" data-id="#c.UserId">Disable</button>
</td>
</tr>
}
var url = '#Url.Action("DisableUser", "UserManagement")';
$('.disable').click(function() {
var row = $(this).closest('tr');
$.post(url, { id: $(this).data('id') }, function(result) {
if(result) {
// for example, remove the row from the table
row.remove();
} else {
// Oops
}
}).fail(function (result) {
// Oops
});
});
and the controller method would be
public JsonResult DisableUser(int id)
{
// Get the User based on id and update it
return Json(true);
// or if the update failed - return Json(null);
}
The simple way to disable a user is to use a Html.ActionLink instead of the form - you should be able to see plenty of examples of this in the template code. The action link could redirect to a confirmation page or you could just disable the user and redirect to a message page saying "the user has been disabled".
I better way is to use AJAX. You can do this with jQuery or you could use the MVC Ajax form or Ajax Action Link. I would recommend that you google MVC Ajax Action Link examples.
You may also want to style the link by setting it's class to the Bootstrap 'btn' class.
There is a way to post a single user
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
<input type="text" name="Username" value="#c.Username">
<input type="text" name="IsEnabled" value="#c.IsEnabled">
<input type="hidden" name="id" value="#c.id">
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
The ideas is same,due to lack of time, please proceed according to your requirements, use the Class Property name as input name, the action will get the value
First of all you must not declare Html.BeginForm inside of any loop to post the object to controller.
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
<input data-id="#c.Id" type="submit" value="Disable" class="clsBtnPost btn btn-primary"/>
</td>
</tr>
}
}
This is for your reference
$(document).ready(function(){
$(".clsBtnPost").click(function(){
var userId = $(this).data("id");
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: 'ControllName/MethodName', //Your Url
data: { 'userId': userId },
success: function () {
alert("successfully called.");
},
failure: function (response) {
alert("Error");
}
});
});
});
You can go with the Ajax Call to pass a single object to controller.

getJSON doesnt call webAPI method

Im new on mvc 4 and webAPI and Im developing my first application on it.It is single page application and using Knockoutjs.I use this walkthrough https://github.com/geersch/KnockoutSpa/
every thing is fine with my webAPI method and it return correct value when I call method with Fiddler.But it never called when I use it from getJson() method.Here is my HTML:
<table id="nqsales" class="table table-striped table-hover table-condensed">
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
<tbody data-bind="foreach: viewModel.nqsales">
<tr>
<td data-bind="text: a"></td>
<td data-bind="text: b"></td>
<td data-bind="text: c"></td>
</tr>
</tbody>
</table>
Javascript
$(function () {
ko.applyBindings(viewModel);
viewModel.loadNqsales();
});
var viewModel = {
nqsales: ko.observableArray([]),
loadNqsales: function () {
var self = this;
$.getJSON(
'#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "NQSale" })',
function (nqsales) {
self.nqsales.removeAll();
$.each(nqsales, function (index, item) {
self.nqsales.push(new nqsale(item));
});
}
);
}
};
function nqsale(nqsale) {
this.expenceFormNo = ko.observable(nqsale.a);
this.orderNo = ko.observable(nqsale.b);
this.date = ko.observable(nqsale.c);
}
WebAPIController
// GET api/NQSale
public IEnumerable<NQSaleDto> GetNQSales()
{
//return db.NQSales.AsEnumerable();
return db.NQSales
.AsEnumerable()
.Select(nqlist => new NQSaleDto(nqlist));
}
Your Url construction of
#Url.RouteUrl("DefaultApi", new { httproute = "", controller =
"NQSale" })
should generate a URL of '/api/NQSale' this means that your controller should be the following:
//The controller name relates to the route
public class NQSaleController : ApiController
{
// The action name relates to the HttpVerb
public IEnumerable<NQSaleDto> Get()
{
//return db.NQSales.AsEnumerable();
return db.NQSales
.AsEnumerable()
.Select(nqlist => new NQSaleDto(nqlist));
}
}
By default, the routing for API is that the name of the controller (NQSaleController) relates to the path and the name of the Action (Get) relates to the HttpVerb.
I would guess that your Url is not being generated correctly. If the Javascript in the View currently or in a seperate file ? #Url only works if its in the view.

Dynamically load data with #Ajax.ActionLink using Skip() and Take()

I am new to ASP.NET MVC, so please help me to solve this, at a first glance, simple issue.
I have a table of students on my View and a Load More link below it. By clicking on this link I need to load more students records with AJAX.
So this is my View (omitted unnecessary data)
<table id="studentTable">
<thead>
...
</thead>
<tbody>
#Html.Partial("_StudentDetailsList", Model)
</tbody>
</table>
#Ajax.ActionLink("Load More", "LoadMoreStudents", new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "studentTable"
})
_StudentDetailsList partial view to display students
#model IEnumerable<MvcApplication1.Models.Student>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
</tr>
}
action
public ActionResult LoadMoreStudents(int skip = 0)
{
return PartialView("_StudentDetailsList", studentRepository.Get(orderBy: s => s.OrderBy(st => st.FirstName).OrderBy(st => st.LastName)).Skip(skip).Take(10));
}
So as you can see, I want to load next 10 students every time, but I have to know how much are already loaded. So the question is: from where should I get this skip parameter which I want to pass to the Action from the View. How can I count on the View how many partial views are already loaded and how many students each partial has? Or is it a bad practice to pass this parameter to action, maybe I should handle it somewhere else (e.g. some service)? Please advice the right way to do this. Thanks.
P.S. Please feel free to edit the caption as I can't come up with appropriate one.
The easiest method would be to create a ViewModel that contains these variables for you.
ViewModel
public class YourViewModel(){
public int skip {get;set;}
public IEnumerable<StudentDetails> StudentDetailList {get;set;}
}
The Controller/Action Method
public ActionResult LoadMoreStudents(int skip = 0)
{
return PartialView("_StudentDetailsList", new YourViewModel{
StudentDetailList = studentRepository.Get(orderBy: s => s.OrderBy(st => st.FirstName).OrderBy(st => st.LastName)).Skip(skip).Take(10),
skip = skip + 10});
}
The View
#Html.Partial("_StudentDetailsList", Model.StudentDetailList)
The Partial View
#model MvcApplication1.Models.YourViewModel
<table id="studentTable">
<thead>
...
</thead>
<tbody>
#foreach (var item in Model.StudentDetailList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
</tr>
}
</tbody>
</table>
#Ajax.ActionLink("Load More", "LoadMoreStudents", new {skip = Model.skip}, new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "studentTable"
})
Basically, you have to be able to dynamically update the Ajax.ActionLink with values coming back from the controller. Here, I have moved it into the PartialView (arguabley you could move the entire table including the fetch more link into the PartialView and have it be even cleaner).
You are on the way. You should replace the Ajax.ActionLink for a link with $.get, and retrieve the skip by counting the numbers of rows at the tbody that you already have:
First replace the ActionLink for:
Load More Students here!
Second, create this function to do the get:
$('mylink').click(function (e) {
e.preventDefault();
//Gets the number of rows that you alredy have loaded
var rowCount = $('studentTable').find('tbody > tr').length;
//The URL for the get, with the number of rows
var url = "http://www.myurl.com/controller/action?skip=" + rowCount;
$.get(url, function (data) {
//Append the content from the partial view to the tbody
$('studentTable').find('tbody').append(data);
});
});
I think that this is a good way as well!
Hopes this help you!

Using jQuery to post back to Controller

In my web page, I have a series of tables that basically just contain rows of information. Each of these is given an id in a for loop and I'm trying to reference them from outside that. I added classes to both the table and a 'Save Changes' button.
Essentially, my goal is for the user to be able to drag and drop rows around, thereby changing the order. Then they can click the 'Save Changes' button and this will post back to the server with the relevant information.
I am having trouble matching up the button to the relevant table and thereby submitting the id's of each row back to the server in an array. I have written the code to be able to be able to get the ids from each of the tables and their current order, but I don't know how to assign this to an array from within the button click jQuery.
Here is the View:
#foreach (var collection in Model.Collections)
{
<h2>#collection.Season</h2>
#Html.ActionLink("Delete Collection", "DeleteCollection", new { controller = "Edit", brand = collection.Brand.Name, season = collection.Season })
#Html.ActionLink("Edit Collection", "EditCollection", new { controller = "Edit", brand = collection.Brand.Name, season = collection.Season })
#Html.ActionLink("Add Image", "CreateImages", new { controller = "Edit", season = collection.Season })
<p>
To change the ordering of images, drag and drop to your desired position and then click the Save Changes button on the appropriate collection.
</p>
<table class="table-collection" id="table-#collection.Id">
<tr class="nodrop nodrag">
<th>
Id
</th>
<th>
Description
</th>
<th>
Image
</th>
<th>
Options
</th>
</tr>
#foreach (var image in collection.Images)
{
<tr id="#collection.Id-#image.Id">
<td class="dragHandle showDragHandle">
#image.Id
</td>
<td>
#image.Description
</td>
<td>
<img src="#Url.Content("~/" + image.Location)" alt="#image.Description" />
</td>
<td>
<ul>
<li>
#Html.ActionLink("Edit", "EditImage", new { controller = "Edit", brand = image.Collection.Brand.Name,
season = image.Collection.Season, imageId = #image.Id } )
</li>
<li>
#Html.ActionLink("Delete", "DeleteImage", new
{
controller = "Edit",
brand = image.Collection.Brand.Name,
season = image.Collection.Season,
imageId = #image.Id
})
</li>
</ul>
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Save Changes" class="save-order" id="saveTable-#collection.Id"/>
</p>
}
Here is the jQuery so far:
$(document).ready(function () {
$(".table-collection").tableDnD();
$(".save-order").click(function (e) {
e.preventDefault();
$.ajax({ url: window.location.href,
type: 'POST',
data: { ids: $("--ASSIGN ARRAY HERE--"
});
The jQuery to iterate through each row is essentially this:
function(table, row) {
var rows = table.tBodies[0].rows;
var debugStr = "Row dropped was "+row.id+". New order: ";
for (var i=0; i<rows.length; i++) {
debugStr += rows[i].id+" ";
}
I see you are using input type submit which is exclusively used to postback forms. What you need to do is wrap every table up in a form with something like this:
#using(Html.BeginForm("Action", "Controller", new{ collectionId = collection.Id }))
{
<input type="submit" value="Save Changes" class="save-order" />
}
Note that this will cause a 'post-back' of the form to Action, Controller. Specify the collection id inside the route values to identify the specific collection.
Do note, you need to add input type hidden with the id's value otherwise the ids' won't get serialised - all you have to specify is the name attribute
<td class="dragHandle showDragHandle">
<input type="hidden" name="ids" value="#(image.Id)" />
#image.Id
</td>
Then you can intercept the call then do it via ajax with:
$(".save-order").click(function(e) {
e.preventDefault();
var form = $(this).closest('form');
if(form.validate()) {
$.post(form.attr('action'), form.serialize(), function() {
alert('The new image order has been saved.');
});
}
return false;
});
The accepting controller action method will probably have this signature
public ActionResult Action(int collectionId, int[] ids)
{
//Do stuff here
return Request.IsAjaxRequest() ? null : View();
}
Now it should support graceful degradation if javascript is disabled (does a normal form submit, otherwise does it via ajax)
Hope this helps :)
You can grab all of the IDs with something like this:
var IDs = [];
$("#mydiv").find("span").each(function(){ IDs.push(this.id); });
In your scenerio, do something like this:
$(document).ready(function ()
{
$(".table-collection").tableDnD();
$(".save-order").click(function (e)
{
var IDs = [];
$("#yourtable").find("draggable-tr-class").each(function(){ IDs.push(this.id); });
e.preventDefault();
$.ajax(
{
url: window.location.href,
type: 'POST',
data: { ids: IDs }
);
}
})
i have been create demo in jsfiddle using json
http://jsfiddle.net/viyancs/4ffb3/11/
if you use like that demo in your server must be get parameter `JSONFile' after that parse this json for what do you want.actually the demo not same with your case but i think you can use this by your logic.

Categories