How to pass Dictionary<Guid,int> to an ASP.NET Core MVC action through an Ajax call? - ajax

I have this function:
reloadCartComponent() {
$.ajax({
url: "Products/UpdateCart",
data: {
o: "Roman"//what type of object will be bound to Dictionary<Guid, int> on the controller's side
},
success: function (data) {
$("#summary").html(data);
}
})
}
And this action method in my controller:
public IActionResult UpdateCart(Dictionary<Guid, int> quantityDictionary = null)
{
return ViewComponent(typeof(CartComponent), quantityDictionary);
}
The method is being called but I have no idea what object I should build on the script side that is going to be correctly converted into Dictionary<Guid, int> on the controller side.

Figured it out:
public IActionResult UpdateCart(Dictionary<Guid,int> productQuantitiyDictionary = null)
{
return ViewComponent(typeof(CartComponent), productQuantitiyDictionary);
}
reloadCartComponent() {
var dict = {};
dict["443597a5-9940-49a8-94d5-3c386e9e6fd1"] = 1;
dict["4b27e0b7-6617-4ba2-b8aa-94751b3d9a7a"] = 2;
$.ajax({
url: "Products/UpdateCart",
data: {
productQuantitiyDictionary : dict
},
success: function (data) {
$("#summary").html(data);
}
})
}

Related

Ajax post method returns undefined in .net mvc

I have this ajax post method in my code that returns undefined. I think its because I have not passed in any data, any help will be appreciated.
I have tried passing the url string using the #Url.Action Helper and passing data in as a parameter in the success parameter in the ajax method.
//jquery ajax post method
function SaveEvent(data) {
$.ajax({
type: "POST",
url: '#Url.Action("Bookings/SaveBooking")',
data: data,
success: function (data) {
if (data.status) {
//Refresh the calender
FetchEventAndRenderCalendar();
$('#myModalSave').modal('hide');
}
},
error: function (error) {
alert('Failed' + error.val );
}
})
}
//controller action
[HttpPost]
public JsonResult SaveBooking(Booking b)
{
var status = false;
using (ApplicationDbContext db = new ApplicationDbContext())
{
if (b.ID > 0)
{
//update the event
var v = db.Bookings.Where(a => a.ID == a.ID);
if (v != null)
{
v.SingleOrDefault().Subject = b.Subject;
v.SingleOrDefault().StartDate = b.StartDate;
v.SingleOrDefault().EndDate = b.EndDate;
v.SingleOrDefault().Description = b.Description;
v.SingleOrDefault().IsFullDay = b.IsFullDay;
v.SingleOrDefault().ThemeColor = b.ThemeColor;
}
else
{
db.Bookings.Add(b);
}
db.SaveChanges();
status = true;
}
}
return new JsonResult { Data = new { status } };
}
Before the ajax call, you should collect the data in object like,
var requestData= {
ModelField1: 'pass the value here',
ModelField2: 'pass the value here')
};
Please note, I have only added two fields but as per your class declaration, you can include all your fields.
it should be like :
function SaveEvent(data) {
$.ajax({
type: "POST",
url: '#Url.Action(Bookings,SaveBooking)',
data: JSON.stringify(requestData),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (data) {
if (data.status) {
//Refresh the calender
FetchEventAndRenderCalendar();
$('#myModalSave').modal('hide');
}
},
error: function (error) {
alert('Failed' + error.val );
}
})
}
Try adding contentType:'Application/json', to your ajax and simply have:
return Json(status);
In your controller instead of JsonResult. As well as this, You will need to pass the data in the ajax code as a stringified Json such as:
data:JSON.stringify(data),
Also, is there nay reason in particular why it's a JsonResult method?

Session not retaining TempData through multiple Ajax calls to Controller

Im trying to work with an object (VideoInfo) in several Action methods. A form sends a viewmodel with a video file to the Upload method in VideoController. It uploads the video and returns a generated guid. When the callback has returned, a new ajax call is made to the Convert method which returns the guid. As seen in the javascript part of Create.cshtml it makes two other Ajax calls, one to the Progress method and one to the Azure method.
When trying to fetch the VideoInfo object in the Azure method and the Progress method, videoInfo is null. Though it successfully retains the data between the Upload method and Convert method. Im using TempData.Peek() so that it shouldnt mark it for deletion.
I can see that the Upload and Convert method is using the same instance of VideoController, where as the Progress and Azure method uses another instance, I guess this could have to do with the problem.
InstanceId when running Upload method: 23ef96fa-c746-4722-ad07-e9e40fc95f29
InstanceId when running Convert method: 23ef96fa-c746-4722-ad07-e9e40fc95f29
InstanceId when running Progress method: 0aba24b2-ccb8-434d-a27d-cc66cb52c466
InstanceId when running Azure method: 0aba24b2-ccb8-434d-a27d-cc66cb52c466
How can I retain data between my Ajax calls in the VideoController?
Why is the instance id same for the first two calls but then it changes?
VideoController.cs
using System;
namespace MediaPortal.Controllers
{
[Authorize(Roles = "Admin")]
public class VideoController : Controller
{
private static Guid InstanceId { get; }
static VideoController()
{
InstanceId = Guid.NewGuid();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(CreateVideoViewModel model)
{
if (ModelState.IsValid)
{
if (model.File != null)
{
Debug.WriteLine("Upload method InstanceId: " + InstanceId.ToString());
// ...
TempData[videoInfo.Id] = videoInfo;
videoInfo.cvvm.File.SaveAs(videoInfo.TempPath);
return Json(new { Successfull = true, Id = videoInfo.Id });
}
return null;
}
return null;
}
[HttpPost]
public ActionResult Convert(string id)
{
Debug.WriteLine("Convert method InstanceId: " + InstanceId.ToString());
// Create new object of FFMpegConvertor
var converter = new FFMpegConverter();
VideoInfo videoInfo = (VideoInfo)TempData.Peek(id);
// ...
return Json(new { Successfull = true, Id = videoInfo.Id });
}
[HttpPost]
public ActionResult Azure(string id)
{
Debug.WriteLine("Azure method InstanceId: " + InstanceId.ToString());
VideoInfo videoInfo = (VideoInfo)TempData[id];
// ...
if (videoUpload != null && thumbnailUpload != null)
{
Video video = new Video
{
Id = videoInfo.Id.ToString(),
Name = videoInfo.cvvm.Name,
ProjectId = videoInfo.cvvm.ProjectId,
Type = "mp4",
VideoUri = videoUpload.Uri.ToString(),
ThumbnailUri = thumbnailUpload.Uri.ToString()
};
db.Videos.Add(video);
db.SaveChanges();
return Json(new { Successful = true, Data = Url.Action("Index", new { projectId = videoInfo.cvvm.ProjectId }) });
}
return null;
}
[HttpPost]
public JsonResult Progress(string id)
{
Debug.WriteLine("Progress method InstanceId: " + InstanceId.ToString());
try
{
VideoInfo videoInfo = (VideoInfo)TempData.Peek(id);
return Json(new { Data = videoInfo.Progress });
}
catch
{
return Json(new { Data = "No Video Information in Dictionary for Id: " + id });
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Create.cshtml (JQuery/Ajax)
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<script>
$.noConflict();
jQuery(document).ready(function ($) {
var bar = $('.progress-bar');
var percent = $('.percent');
$('#Myform').ajaxForm({
type: "POST",
contentType: "application/json; charset=utf-8",
beforeSend: function () {
var percentVal = '0%';
bar.width(percentVal);
percent.html(percentVal);
},
uploadProgress: function (event, position, total, percentComplete) {
var percentVal = percentComplete + '%';
bar.width(percentVal);
percent.html(percentVal);
},
complete: function (uploadStatus) {
console.log("Upload finished for video with Id: " + JSON.parse(uploadStatus.responseText).Id);
setTimeout(function () { progress(uploadStatus) }, 1000);
$.ajax({
type: "POST",
url: '#Url.Action("Convert", "Video")?id=' + JSON.parse(uploadStatus.responseText).Id,
dataType: "json",
contentType: "application/json; charset=utf-8",
complete: function (convertStatus) {
console.log("Conversion finished for video with Id: " + JSON.parse(convertStatus.responseText).Id);
$.ajax({
type: "POST",
url: '#Url.Action("Azure", "Video")?id=' + JSON.parse(convertStatus.responseText).Id,
dataType: "json",
contentType: "application/json; charset=utf-8",
complete: function (azureStatus) {
window.location.href = JSON.parse(azureStatus.responseText).Data;
}
});
}
});
}
});
function progress(uploadStatus) {
$.ajax({
type: "POST",
url: '#Url.Action("Progress", "Video")?id=' + JSON.parse(uploadStatus.responseText).Id,
dataType: "json",
contentType: "application/json; charset=utf-8",
complete: function (progressStatus) {
console.log("Progress: " + JSON.parse(progressStatus.responseText).Data);
if (JSON.parse(progressStatus.responseText).Data < 100) {
setTimeout(function () { progress(uploadStatus) }, 1000);
}
else if (JSON.parse(progressStatus.responseText).Data >= 100) {
console.log("Video Conversion Completed");
}
else {
console.log("Something went wrong");
}
}
})
}
});
</script>
It seems IIS Express was the problem. I cant say why though. I realized this because when I deployed the project to my production server in Azure it all worked fine.
So instead of using IIS Express I installed IIS on my development machine, works perfectly after that.

To what mvc object do I bind this kind of javascript array?

I am populating an array with an [int,bool]:
$.each(ownedChecked, function (key, val) {
/*code to set id as int and paintedTrue as bool*/
ownedIDs.push([id,paintedTrue]);
});
I create a var that will be stringified:
var saveData = { OwnedListEntryIDs:ownedIDs };
//send data
$.ajax({
url: '/ListEntry/OwnedModels',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(saveData),
success: function (data) { alert('Update success: ' + data); },
failure: function (data) { alert('Update failed: ' + data); }
});
Here is the ViewModel:
public class OwnedPaintedSave
{
//collection contains a ListEntryID and a bool indicating whether it is painted or not
public Dictionary<int,bool> OwnedListEntryIDs { get; set; }
}
Once in the controller method, ModelState.Isvalid passes and code gets to the foreach loop, but models is always NULL:
[HttpPost]
public JsonResult OwnedModels(OwnedPaintedSave models)
{
if (ModelState.IsValid)
{
foreach (var id in models.OwnedListEntryIDs)
{ }
}
}
I tried setting traditional:true in the $.ajax method, but same issue. I'm guessing the Dictionary in the ViewModel is not what I need to bind to, but I'm not sure what it should be. Thanks for any help.
I gave the OwnedPaintedSave object two int[] properties, one for OwnedIDs and one for PaintedIDs.
I think the problem lies in your controller. You are passing var saveData = { OwnedListEntryIDs:ownedIDs }; (essentially a Dictionary<int, bool> - yes?) as your data but the parameter in the action result is of type OwnedPaintedSave
I think if you change your controller parameter to Dictionary<int, bool> ownedListEntryIDs and don't stringify the javascript object, the framework is clever enough to serialize and deserialize your object. So you would have this...
var saveData = { OwnedListEntryIDs:ownedIDs };
//send data
$.ajax({
url: '/ListEntry/OwnedModels',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: saveData,
success: function (data) { alert('Update success: ' + data); },
failure: function (data) { alert('Update failed: ' + data); }
});
And then this in your controller...
[HttpPost]
public JsonResult OwnedModels(Dictionary<int, bool> ownedListEntryIDs)
{
if (ModelState.IsValid)
{
foreach (var id in ownedListEntryIDs)
{ }
}
}

Pass serialized form to Action and bind to model

I am trying to bind model received from Ajax call but that do not work. Maybe someone could help me?
I am calling ValidateFile Action using Ajax
$.ajax({
url: '#Url.Action("ValidateFile", "Converter")',
data: ({ file: fileName, formData: serializedForm }),
type: 'POST',
success: function (response) {
if (response.result) {
} else {
RemoveFile(fileName);
}
}
});
The Fiddler show such query
file=!!!SP+Design!!!.txt&formData%5BEmail%5D=tomas%40mydomain.com
I receive data in my Action with file parameter populated but formData.Email property is always Null
[HttpPost]
public JsonResult ValidateFile(string file, UploadOptionModel formData)
{
}
My UploadOptionModel model
namespace PC.Models
{
public class UploadOptionModel
{
public string Email { get; set; }
}
}
Form which I am trying to serialize
#model PC.Models.UploadOptionModel
#using (Html.BeginForm())
{
#Html.EditorFor(p => p.Email)
}
JS Serialization function
function serializeForm() {
var data = $("form").serializeArray();
var formData = {};
for (var i = 0; i < data.length; i++) {
formData[data[i].name] = data[i].value;
}
return formData;
}
You need to JSON encode the data and set the content type to JSON for the model binder to work with JSON. So try this:
$.ajax({
url: '#Url.Action("ValidateFile", "Converter")',
data: JSON.stringify({ file: fileName, formData: serializedForm }),
contentType: 'application/json',
type: 'POST',
success: function (response) {
if (response.result) {
} else {
RemoveFile(fileName);
}
}
});

Passing complex objects to Action in asp.net MVC from jquery ajax

I have the following domain model defined:
public class myModel {
public string Prop1 {get;set;}
public string Prop2 {get;set;}
public List<myClass> ListofStuff {get;set;}
}
public myClass {
public string Id{get;set;}
public string Name{get;set;}
}
Then I have the controller action defined as follows:
[HttpPost]
public ActionResult Save(MyModel someModel )
{
//do the saving
}
I call the above action from my JS code using jquery ajax
var someModel = { Prop1: "somevalue1",
Prop2: "someothervalue",
ListofStuff: [{Id: "11", Name:"Johnny"}, {Id:"22", Name:"Jamie"}]
};
$.ajax({
contentType: 'application/json, charset=utf-8',
type: "POST",
url: "/myController/Save",
data: JSON.stringify({someModel: someModel}),
cache: false,
dataType: "json",
success: function () {
alert('success!');
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('error');
}
});
EDITED:
When I run the above code I get error handler gets executed. I tried to installed Firebug but my FF is version 8 and it couldn't install it. So I am not sure what the error is or how to see what it is for that matter.
What am I doing wrong?
I resolved the issue. MyClass needed to have a parameter-less constructor in order for the binding to work properly.
You can use following method for this purpose.
$.postifyData = function (value) {
var result = {};
var buildResult = function (object, prefix) {
for (var key in object) {
var postKey = isFinite(key)
? (prefix != "" ? prefix : "") + "[" + key + "]"
: (prefix != "" ? prefix + "." : "") + key;
switch (typeof (object[key])) {
case "number": case "string": case "boolean":
result[postKey] = object[key];
break;
case "object":
if (object[key] != null) {
if (object[key].toUTCString) result[postKey] = object[key].toUTCString().replace("UTC", "GMT");
else buildResult(object[key], postKey != "" ? postKey : key);
}
}
}
};
buildResult(value, "");
return result;
}
And after that you can pass model like.
var someModel = new Object();
someModel.Prop1 = "Pp1";
someModel.Prop2 = "PP2";
someModel.ListofStuff = new Array();
//Use for each loop to bind list like
for (var i = 0; i < 2; i++) {
var childObj = new Object();
childObj.Id = "123" + i;
childObj.Name = "Pankaj"
someModel.ListofStuff.push(childObj);
}
$.ajax({
type: "POST",
url: "/home/test",
data: $.postifyData(someModel),
cache: false,
dataType: "json",
success: function () {
alert('success!');
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert('error');
}
});
i have checked it on my own and this is working fine.

Resources