Passing IEnumerable through Json - asp.net-mvc

I'm making a "Like" button in a simple comment database MVC program.
I'm passins the ID of the comment through to a ActionResult in the HomeController when I hover over the "Like" button. The problem (I think) is that I don't know how to pass the IEnumerable list of Likes to the ajax.
The script and HTML part:
HTML:
Like this
Script:
$(".likes").hover(function (event) {
var Liker = { "CID": event.target.id };
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Home/ShowLike/",
data: JSON.stringify(Liker),
dataType: "json",
success: function (data) {
$.each(data.Name, function (value) {
alert(value);
});
},
error: function (xhr, err) {
// Note: just for debugging purposes!
alert("readyState: " + xhr.readyState +
"\nstatus: " + xhr.status);
alert("responseText: " + xhr.responseText);
}
});
});
HomeController -> ShowLike
[HttpPost]
public ActionResult ShowLike(Liker ids)
{
LikesRepository lkrep = new LikesRepository();
IEnumerable<Like> list = lkrep.GetLikes(ids.CID);
return Json(list);
}
LikesRepository
public class LikesRepository
{
CommentDBDataContext m_db = new CommentDBDataContext();
public IEnumerable<Like> GetLikes(int iden)
{
var result = from c in m_db.Likes
where c.CID == iden
orderby c.Name ascending
select c;
return result;
}
public void AddLike(Like c)
{
m_db.Likes.InsertOnSubmit(c);
m_db.SubmitChanges(); //This works
}
}

After diving into the problem more, we found it was actually triggering an internal server error (500). This was caused by a circular reference from serializing the LINQ to SQL objects to JSON. This issue is discussed several times...
How to remove circular reference in Entity Framework?
How did I solve the Json serializing circular reference error?
Circular Reference exception with JSON Serialisation with MVC3 and EF4 CTP5w
An alternate solution is to return the data as lists of strings as they only required the names.

Related

How to pass JSON data (list created using Jquery) to an action in controller in MVC?

I have a function in jquery which creates a list of values being selected from a checkbox. Now I want to have this list in my controller action. I have converted this list to JSON but I am not able to pass it to the controller. I also tried creating a custom model corresponding to the json data.
Jquery Code
$("button").click(function () {
//alert("clicked");
var obj = {};
//var tempRadio = [];
for (var i = 1; i <= globalVar; i++) {
if ($("#" + i).prop("checked") == true) {
obj[i] = $('input[class=' + i + ']:checked').val();
}
}
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '#Url.Action("SkillAdd","User")',
data: JSON.stringify(obj),
//data: hello,
error:function ()
{
alert("Error");
},
success: function () {
alert(JSON.stringify(obj));
}
});
});
Controller Code
public ActionResult SkillAdd(List<string> Id, List<string> Name)
{
return View();
}
Controller Code with Custom Model
public ActionResult SkillAdd(List<MyModel> object)
{
return View();
}
You have an object in javascript but you need to create an array so that it can be mapped to List at post. So change your js code to be :
var obj = []; // it's array now
and then you will add items in it like in your loop:
obj.push( $('input[class=' + i + ']:checked').val());
and in your ajax call name the parameter what you have in your controller action:
data:{ Id : obj }
and now you can have a parameter in action method List<string> which would hold the data posted by ajax call.
public ActionResult SkillAdd(List<string> Id)
{
return View();
}
Hope it helps.

MVC.NET old data comes back after refresh of screen

I have created jquery ajax call in my view page and this successfully posts data.
However, if I press ctrl+F5 to force browser refresh, old data comes back.
If I close down browser and revisit the site, it now shows updated data.
Following is my action in controller.
I am fairly new to MVC.NET and I am sure there is something I am missing.
Can you please advise?
I truly appreciate in advance.
[HttpPost]
[ActionName("EditCategory")]
public ActionResult EditCategory(miniCateObj[] items)
{
int id = int.Parse(items[0].id.Replace("'",""));
string newName = items[0].cateName.Replace("'", "");
ModelState.Remove("CategoryName");
ModelState.Clear();
Category model = _categoryService.GetCategoryByCategoryId( id);
model.CategoryName = newName;
_categoryService.UpdateCategorySite(model);
return View(model);
}
And following is the ajax call I'm using
$.ajax({
type: "POST",
url: '#Url.Action("EditCategory", "CloSite")',
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
"items": [{
"id": "'" + id + "'",
"cateName": "'" + obj.val() + "'"
}]
}),
dataType: "application/xml-urlencode",
success: function () {
alert('Success');
},
error: function (xhr, textStatus, error) {
console.log(xhr.statusText);
console.log(textStatus);
console.log(error);
}
});
Also, I'm not using ORM / Entity Framework. I had problem the projects .savechanges() not updating(in Entity Framework), so I ended up writing direct SQL to update as following,
public int UpdateByCategoryId(int categoryId, string cateName) {
using(var connection = this.Context.Database.Connection) {
if (string.IsNullOrEmpty(connection.ConnectionString)) connection.ConnectionString = ConfigurationHelper.ConnectionString;
var query = string.Format(#"UPDATE Categories set CategoryName = '{0}' where Id = {1}", cateName, categoryId);
return connection.Execute(query);
}
}

How to bind multiple classes in web api

$.ajax({
type: "POST",
data: "{myEvents: " + JSON.stringify(myEvents) + ", myRecurrences: " + JSON.stringify(myRecurrences) + "}",
url: "/signupadmin/api/SignupAdminAPI/SaveEvent",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (obj)
{
alert("success");
},
error: function (obj)
{
alert(obj.error());
}
});
[HttpPost]
public bool SaveEvent(EvancedEventData myEvents,RecurrenceData myRecurrences)
{
return true;
}
I have used two class files(evancedeventdata and recurrencedata) as mentioned and trying to bind the json objects to two classes from ajax but it throws the error "cannot bind multiple parameters" can you one suggest a reason why its not getting binded
Would be better to support questions like this with examples of the data, such as the JSON string you're posting and the Exception report.
However, try creating a wrapper class to encapsulate myEvents and myRecurrences, something like:
public class MyWrapperClass
{
EvancedEventData myEvents {get;set;}
RecurrenceData myRecurrences {get;set;}
}
Then change your POST action to expect the wrapper class:
[HttpPost]
public bool SaveEvent(MyWrapperClass data)
{
return true;
}
Then you should be able to read the data like this:
[HttpPost]
public bool SaveEvent(MyWrapperClass data)
{
var events = data.myEvents
//do stuff with the events
var recurrences = data.myRecurrences
//do stuff with the recurrences
return true;
}
(above is just an example - you no need to create and use separate variables to access the data).
Hope this helps.
Ben

How to submit local jqgrid data and form input elements

Page contains single form with input elements and jqgrid data.
jqGrid data is retrieved in json using loadonce: true option.
Data is edited locally.
How to submit all this data if submit button is pressed?
Has jqGrid any method which can help to submit all data from all rows. jqGrid - How to edit and save multiple rows at once? mentions that jQuery ajax form plugin should be used but I havent found any sample.
jqGrid probably holds retrieved json table in element. In this case form plugin is not capable read this data.
How to get and submit all data retrieved using loadonce: true and edited?
Update1
Based on Oleg answer I tried:
function SaveDocument() {
var gridData = $("#grid").jqGrid('getGridParam','data');
var postData = JSON.stringify(gridData);
$('#_detail').val( postData );
var res = $("#Form").serializeArray();
$.ajax({ type: "POST",
url: 'Edit'
data : res
});
}
}
aspx page:
<form id="Form" class='form-fields'>
.... other form fields
<input name='_detail' id='_detail' type='hidden' />
</form>
<div id="grid1container" style="width: 100%">
<table id="grid">
</table>
</div>
In ASP.NET MVC2 Controller Edit method I tried to parse result using
public JsonResult Edit(string _detail) {
var order = new Order();
UpdateModel(order, new HtmlDecodeValueProviderFromLocalizedData(ControllerContext));
var serializer = new JavaScriptSerializer();
var details = serializer.Deserialize<List<OrderDetails>>>(_detail);
}
Exception occurs in Deserialize() call. Decimal and date properties are passed in localized format but it looks like Deserialize() does not parse
localized strings and there is no way to force it to use converter like HtmlDecodeValueProviderFromLocalizedData passed to UpdateModel.
How to fix ?
Is is reasonable/how to convert _detail parameter into NameValue collection and then use UpdateModel to update details, use some other deserialize or other idea ?
Update 2.
Decimal and Date CurrentUICulture values are present in form and in jqGrid data. Sample provided handles them in form OK but fails for jqGrid data.
This controller should handle different entity types, form fields and jqgrid columns can defined at runtime. So using hard-coded names is not possible.
Based on Oleg reply I tried to override decimal conversion by creating converter
public class LocalizedTypeConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new Type[] { typeof(decimal) });
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type,
JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
if (type == typeof(decimal))
return decimal.Parse(dictionary["resources"].ToString(), CultureInfo.CurrentCulture);
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new InvalidOperationException("We only Deserialize");
}
}
But conversion still causes exception
0,0000 is not valid value for decimal. It looks like decimal converter cannot overridden. How to fix ?
First of all you can get all local data from the jqGrid with respect of
var localGridData = $("#list").jqGrid('getGridParam','data');
If you will need to send only subset of rows of the grid, like the selected rows only, you can need to get _index:
var idsToDataIndex = $("#list").jqGrid('getGridParam','_index');
To send the data to the server you can use the following function for example
var sendData = function(data) {
var dataToSend = JSON.stringify(data);
alert("The following data are sending to the server:\n" + dataToSend);
$.ajax({
type: "POST",
url: "yourServerUrl",
dataType:"json",
data: dataToSend,
contentType: "application/json; charset=utf-8",
success: function(response, textStatus, jqXHR) {
// display an success message if needed
alert("success");
},
error: function(jqXHR, textStatus, errorThrown) {
// display an error message in any way
alert("error");
}
});
};
In the demo you will find a little more sophisticated example having two buttons: "Send all grid contain", "Send selected rows". The corresponding code is below
$("#sendAll").click(function(){
var localGridData = grid.jqGrid('getGridParam','data');
sendData(localGridData);
});
$("#sendSelected").click(function(){
var localGridData = grid.jqGrid('getGridParam','data'),
idsToDataIndex = grid.jqGrid('getGridParam','_index'),
selRows = grid.jqGrid('getGridParam','selarrrow'),
dataToSend = [], i, l=selRows.length;
for (i=0; i<l; i++) {
dataToSend.push(localGridData[idsToDataIndex[selRows[i]]]);
}
sendData(dataToSend);
});
where
var grid = $("#list"),
decodeErrorMessage = function(jqXHR, textStatus, errorThrown) {
var html, errorInfo, i, errorText = textStatus + '\n<br />' + errorThrown;
if (jqXHR.responseText.charAt(0) === '[') {
try {
errorInfo = $.parseJSON(jqXHR.responseText);
errorText = "";
for (i=0; i<errorInfo.length; i++) {
if (errorText.length !== 0) {
errorText += "<hr/>";
}
errorText += errorInfo[i].Source + ": " + errorInfo[i].Message;
}
}
catch (e) { }
} else {
html = /<body.*?>([\s\S]*)<\/body>/i.exec(jqXHR.responseText);
if (html !== null && html.length > 1) {
errorText = html[1];
}
}
return errorText;
},
sendData = function(data) {
var dataToSend = JSON.stringify(data);
alert("The following data are sending to the server:\n"+dataToSend);
$.ajax({
type: "POST",
url: "yourServerUrl",
dataType:"json",
data: dataToSend,
contentType: "application/json; charset=utf-8",
success: function(response, textStatus, jqXHR) {
// remove error div if exist
$('#' + grid[0].id + '_err').remove();
alert("success");
},
error: function(jqXHR, textStatus, errorThrown) {
// remove error div if exist
$('#' + grid[0].id + '_err').remove();
// insert div with the error description before the grid
grid.closest('div.ui-jqgrid').before(
'<div id="' + grid[0].id + '_err" style="max-width:' + grid[0].style.width +
';"><div class="ui-state-error ui-corner-all" style="padding:0.7em;float:left;"><span class="ui-icon ui-icon-alert" ' +
'style="float:left; margin-right: .3em;"></span><span style="clear:left">' +
decodeErrorMessage(jqXHR, textStatus, errorThrown) + '</span></div><div style="clear:left"/></div>');
}
});
};
I think, that more difficult and more complex problem you will become on the server. In case of concurrency errors, but I wrote you about the problems before. Exactly because of the problems I personally would never implement saving of multiple rows on the server.
UPDATED: To get data from the form you can use jQuery.serialize. You should use name attribute for all fields in the form which you want to serialize. All data which you need to send are
var allData = {
localGridData: grid.jqGrid('getGridParam','data'),
formData: $("#formid").serialize()
};
You can send the data exactly like I described before: sendData(allData).

ASP.NET MVC 3 Treeview

I need to display a Treeview in my MVC3 application. There will be a self referencing hierarchical table (Folders) and another table linked to it (Documents.) (So Folders can have N-subFolders and any folder/sub folder can have many documents.)
I have looked into using third party vendors such as Telerik, DJME and MVC Controls Toolkit. While all nice packages, I'm uneasy about the licences, and since i'm new to MVC (and programming in general,) I find their documentation lacking to get the right display working.
I've also looked at the heavily referenced blogs on TreeViews:
TreeViewHelper
and the Recursive Partial View
In addition to the other less referenced articles (The top 3 are also very informative):
http://tpeczek.com/2010/01/asynchronous-treeview-in-aspnet-mvc.html
http://mikehadlow.blogspot.com/2008/10/rendering-tree-view-using-mvc-framework.html
http://www.tek-tips.com/viewthread.cfm?qid=1637392&page=4
http://weblogs.asp.net/jigardesai/archive/2008/02/04/display-hierarchical-data-in-asp-net-mvc-framework.aspx
http://www.jigar.net/articles/viewhtmlcontent311.aspx
http://help.syncfusion.com/ug_82/ASP.NETMVCUI_Tools/CreatingATreeViewControl.html
I would like to use either the TreeViewHelper or the Recursive Partial View Method.
However, in the TreeViewHelper, I can't make it pull data from the second table (ie. I can only make it list the Files, but I'm not sure how to have it list the Documents for each File.)
For the Recursive Partial View, I'm still at a loss in how to convert this to MVC3 and also general implementation. I did find a post (forums.asp.net/t/1652809.aspx/1?treeview+with+mvc+3) that gives an explanation of how to convert a bit of it to MVC3, but i'm still unclear of what to do with it. I keep getting the error for the Partial view: Cannot implicitly Convert type 'void' to type 'object'
Like I said before I'm new to MVC3 and would like insight in which method would work best for my scenario and how to implement it.
In case anyone is wondering, the way I solved this problem was to use a recursive partial view. The problem I has having with it was that I didn't have the self referencing relationship set up in SQL/EF (I just had the ParentID field which wasn't linked to the Primary Key.) I also integrated jsTree as this has a lot of slick functionality such as search.
Like I said in the comment above, #Html.Action and #Html.Partial work instead of #Html.RenderAction and #Html.RenderPartial.
give a look to the edit/add/delete/node move templated TreeView of my Mvc Controls Toolkit here: http://mvccontrolstoolkit.codeplex.com/wikipage?title=TreeView
$(document).ready(function () {
BindChart();
});
function BindChart() {
$("#org").jOrgChart({
chartElement: '#chart',
dragAndDrop: true
});
}
$(".cardadd").live("click", function ()
{
var data = { id: 0 , ParentId:$(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardedit").live("click", function () {
var data = { id: $(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardremove").live("click", function () {
});
function OpenForminWindow(popupId, targetDivId, formid, url, data, callbackfunc, heigth, width) {
$.ajax({
type: "GET",
url: url,
data: data,
cache: false,
success: function (data) {
$('#' + targetDivId).html(data);
$('#' + formid).removeData('validator');
$('#' + formid).removeData('unobtrusiveValidation');
$('#' + formid).each(function () { $.data($(this)[0], 'validator', false); }); //enable to display the error messages
$.validator.unobtrusive.parse('#' + formid);
if (callbackfunc)
return callbackfunc();
}
});
$("#" + popupId).dialog({
modal: true,
height: heigth,
width: width,
beforeClose: function (event, ui) {
if (typeof refresh !== 'undefined' && refresh == true)
ReloadCurrentPage();
}
});
}
$('#frmChartMember').live('submit', function (e) {
SubmitAjaxForm($(this).attr('id'), chart.AddMember, ReloadChart);
e.preventDefault();
});
function SubmitAjaxForm(formId, url, callBack) {
$.ajax({
url: url,
type: 'post',
cache: false,
data: $('#' + formId).serialize(),
success: function (data) {
return callBack(data);
},
});
}
function ReloadChart(result) {
ClosePopup('divfrmChartMember');
$.ajax({
type: 'GET',
url: chart.ChartList,
cache: false,
success: function (result) {
$("#orgChart").html(result);
BindChart();
}
});
}
function ClosePopup(divid) {
$("#" + divid).dialog("close");
}
public class ChartController : Controller
{
//
// GET: /Chart/
ChartContext ctx = new ChartContext();
public ActionResult Index()
{
return View();
}
public ActionResult OrgChart()
{
return PartialView("_OrgChart", ctx.Cards.ToList());
}
public ActionResult ChartMember(int id, int? ParentId = null)
{
Card card = new Card();
if (id > 0)
card = ctx.Cards.Find(id);
else
card.ParentId = ParentId;
return PartialView("_ChartMember", card);
}
public ActionResult SaveMember(Card card)
{
if (card.id == 0)
ctx.Cards.Add(card);
else
ctx.Entry(card).State = System.Data.EntityState.Modified;
ctx.SaveChanges();
return Json(true, JsonRequestBehavior.AllowGet);
}
}

Resources