call an c# mvc controller method from jquery using $.getJson Method - c#

I'm clearly missing something but can't for the life of me see what it is so would appreciate if anyone could point out my error.
I have a simple details page with a form to add comments to the selected detail.
I have a view with the following formed contained within it:
#using (Html.BeginForm("Details", "Home", FormMethod.Post, new { id ="commentForm" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.NewComment.Name);
#Html.TextBoxFor(model => model.NewComment.Name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewComment.Body);
#Html.TextAreaFor(model => model.NewComment.Body, new { #class = "form-control" })
</div>
<input type="submit" class="btn btn-primary" value="Add Comment" />
}
This view then calls the following c# controller method:
[HttpPost]
public ActionResult Details(int id,DetailsViewModel model)
{
if (!ModelState.IsValid)
return View(model);
var content =_data.First(c => c.Id == id);
content.Comments.Add(model.NewComment);
return View(new DetailsViewModel(content));
}
If I use the form without adding any additional code to catch the submit with jquery then this all works correctly.
When i add the following JQuery code to the page then the above server code is not executed (I know i am not actually returning any json in the above method but if the method is not executed that seems redundant for now?):
$(document).ready(function () {
$("#commentForm").submit(function (event) {
event.preventDefault();
var url = $(this).attr('action');
$.getJSON(url, $(this).serialize(), function (comment) {
alert(comment)
});
});
});
If is also worth noting that if i add any alerts around the getjson call then these all fire correctly.
Does anyone have any ideas about what i'm doing wrong?

When you are using .getJSON it makes a GET request, and your Details method only answers POST requests.
Try this instead:
$.ajax({
type: "POST",
url: url,
data: $(this).serialize(),
success: function(comment) {
alert(comment);
}
});

Try posting to the controller.
$.getJSON is performing a http get under the covers. Your controller endpoint is expecting a post and will not accept a http get.
Here is a function(blog reference) that will provide the same functionality:
(function ($) {
$.postJSON = function (url, data) {
var o = {
url: url,
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8'
};
if (data !== undefined) {
o.data = JSON.stringify(data);
}
return $.ajax(o);
};
} (jQuery));
Simply add this somewhere after your jQuery include.

use $.post() instead, when you use FormMethod.Post : http://api.jquery.com/jQuery.post/

Related

Serialize CSV files for ajax request in ASP app

I've a form composed by a Kendo combobox and a Kendo file upload:
#using (Html.BeginForm("Methode", "controller", FormMethod.Post))
{
<p> Select country </p>
#(Html.Kendo().DropDownList().Name("country")
.DataTextField("Text").DataValueField("Value")
.DataSource(source => { source.Read(read => {
read.Action("GetMonth", "BudgetStage");
});
}))
<p> Select a .CSV file: </p>
#(Html.Kendo().Upload()
.Name("files")
.HtmlAttributes(new { accept = ".csv" })
)
<input type="submit" value="Submit" class="k-button k-primary" />
<input type="button" value="Ajax" class="k-button k-primary" onclick="postData(this)
}
Here is my C# controller code:
public JsonResult methode(IEnumerable<HttpPostedFileBase> files, string country)
{
return Json("It's OK !", JsonRequestBehavior.AllowGet);
}
This works when I click on the submit button, the controller receives files and string values but I need to display the Json returned value from my page.
To do that, I use this ajax function:
function postData(button) {
var form = $(button).parents('form');
if (form.kendoValidator().data("kendoValidator").validate()) {
$.ajax({
type: "POST",
url: form.attr('action'),
data: form.serialize(),
error: function (xhr, status, error) {
alert(error);
},
success: function (response) {
alert(response);
}
});
return false;
}
return false;
}
When I click on the Ajax button, the backend method is call, return the Json value and my JS code display it.
The problem is I receive the string value but not the selected CSV file (null).
How can I send the file and the string and display the returned Json value ?
Thank you in advance.
The problem is that form.serialize() doesn't really work in this scenario. With kendo it shouldn't be different to ASP.NET MVC + JavaScript, so basically, this should help you.

Need to simulate an #Html.action() on a button click

I'm having some issues with updating a partial view in my index view. Basically, based on a click, I would like to have updated information.
//controller
public ActionResult Index()
{
var filteredObservations = getFilteredObservationSessions().ToList();
var observationManagementVm = new ObservationManagementVM(filteredObservations);
return View(observationManagementVm);
}
public ActionResult indexPagedSummaries(int? page, List<ObservationSessionModel> data)
{
var alreadyFilteredObservations = data;
int PageSize = 10;
int PageNumber = (page ?? 1);
return PartialView(alreadyFilteredObservations.ToPagedList(PageNumber, PageSize));
}
My main view
//index.cshtml
#model AF.Web.ViewModels.ObservationManagementVM
....
<div id="testsim">
#Html.Action("indexPagedSummaries", new { data = Model.ObservationSessions })
</div>
<input id="new-view" value="Sessions" type="button" />
<script>
$("#new-view").click(function() {
$.ajax({
type: "GET",
data: { data: "#Model.FeedBackSessions" },
url: '#Url.Action("indexPagedSummaries")',
cache: false,
async: true,
success: function (result) {
console.log(result);
$('#testsim').html(result);
$('#testsim').show();
}
});
});
</script>
....
And my partial view
//indexPagedSummaries.cshtml
#model PagedList.IPagedList<AF.Services.Observations.ObservationSessionModel>
#using (Html.BeginForm("indexPagedSummaries"))
{
<ol class="vList vList_md js-filterItems">
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item)
}
</ol>
<div>
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("Index",
new { page }))
</div>
}
Html.Action() returns what I want perfectly, but it doesn't seem to be able to be triggered by a button click.
So, I'm not getting any errors, but the url doesn't give any data back. When I try to run the Observation/indexPagedSummary url without passing in data, I get a System.ArgumentNullException error, so I'm assuming that something is being transferred to the view model. Any help would be so appreciated.
Have not run your code but I believe it is because you are not sending the data along with the #Url.Action
Main View:
//index.cshtml
#model AF.Web.ViewModels.ObservationManagementVM
....
<div id="testsim">
#Html.Action("indexPagedSummaries", new { data = Model.ObservationSessions })
</div>
<input id="new-view" value="Sessions" type="button" />
<script>
$("#new-view").click(function() {
$.ajax({
type: "GET",
data: { data: "#Model.FeedBackSessions" },
url: '#Url.Action("indexPagedSummaries", "[Controller Name]", new { data = Model.ObservationSessions})',
cache: false,
async: true,
success: function (result) {
console.log(result);
$('#testsim').html(result);
$('#testsim').show();
}
});
});
</script>
If that doesn't help I have had issues when I have had a content-type mismatch or a datatype mismatch. You may need to add those to you ajax request.
Change your ajax data line to this:
data: { data: JSON.stringify(#Model.FeedBackSessions) },
You may also need to add these lines to the ajax:
dataType: 'json',
contentType: 'application/json; charset=utf-8',
You can see in one of your comments above that the current URL is being formed with a description of the List Object, rather than the contents of it:
http://localhost:60985/Observation/indexPagedSummaries?data=System.Collections.Generic.List%601%5BAF.Services.Observations.ObservationSessionModel%5D&data=System.Collections.Generic.List%601%5BAF.Services.Observations.ObservationSessionModel%5D&_=1482453264080
I'm not sure if there's a better way, but you may even have to manually get the model data into Javascript before posting it.
eg:
<script>
var temp = [];
#foreach (var item in Model.FeedBackSessions){
#:temp.push(#item);
}
</script>
and then data: { data: JSON.stringify(temp) },

Display a loading screen using anything

I sometimes have operation that takes a while to compute. I would like to be able to display something, like a kind of grey layer covering everything, or a loading screen, while the operation computes. But I frankly have no idea how to do it.
I'm building an MVC app using MVC4, I'm beginning with jQuery and opened to any suggestions. How might I do that?
EDIT
Here's a sample of page I've been building:
<h2>Load cards</h2>
<script type="text/javascript">
$(document).ready(function () {
$("form").submit(function (event) {
event.preventDefault();
alert("event prevented"); // Code goes here
//display loading
$("#loadingDialog").dialog("open");
alert("dialog opened"); // Never reaches here.
$.ajax({
type: $('#myForm').attr('method'),
url: $('#myForm').attr('action'),
data: $('#myForm').serialize(),
accept: 'application/json',
dataType: "json",
error: function (xhr, status, error) {
//handle error
$("#loadingDialog").dialog("close");
},
success: function (response) {
$("#loadingDialog").dialog("close");
}
});
alert("ajax mode ended");
});
});
</script>
#using (Html.BeginForm())
{
<div class="formStyle">
<div class="defaultBaseStyle bigFontSize">
<label>
Select a Set to import from:
</label>
</div>
<div class="defaultBaseStyle baseFontSize">
Set: #Html.DropDownList("_setName", "--- Select a Set")<br/>
</div>
<div id="buttonField" class="formStyle">
<input type="submit" value="Create List" name="_submitButton" class="createList"/><br/>
</div>
</div>
}
Here's a snippet of code from my javascript file:
$(document).ready(function ()
{
$(".createList").click(function() {
return confirm("The process of creating all the cards takes some time. " +
"Do you wish to proceed?");
});
}
As a bonus (this is not mandatory), I'd like it to be displayed after the user has confirmed, if it is possible. else I do not mind replacing this code.
EDIT
Following Rob's suggestion below, here's my controller method:
[HttpPost]
public JsonResult LoadCards(string _submitButton, string _cardSetName)
{
return Json(true);
}
And here's the "old" ActionResult method:
[HttpPost]
public ActionResult LoadCards(string _submitButton, string _setName)
{
// Do Work
PopulateCardSetDDL();
return View();
}
As of now the code never reaches the Json method. It does enter the ajax method up there (see updated code), but I don't know how to make this work out.
We hide the main content, while displaying an indicator. Then we swap them out after everything is loaded. jsfiddle
HTML
<div>
<div class="wilma">Actual content</div>
<img class="fred" src="http://harpers.org/wp-content/themes/harpers/images/ajax_loader.gif" />
</div>
CSS
.fred {
width:50px;
}
.wilma {
display: none;
}
jQuery
$(document).ready(function () {
$('.fred').fadeOut();
$('.wilma').fadeIn();
});
First you want to have jQuery "intercept" the form post. You will then let jQuery take care of posting the form data using ajax:
$("form").submit(function (event) {
event.preventDefault();
//display loading
$("#loadingDialog").dialog("open");
$.ajax({
type: $('#myForm').attr('method'),
url: $('#myForm').attr('action'),
data: $('#myForm').serialize(),
accept: 'application/json',
dataType: "json",
error: function (xhr, status, error) {
//handle error
$("#loadingDialog").dialog("close");
},
success: function (response) {
$("#loadingDialog").dialog("close");
}
});
});
More information on the $.ajax() method is here: http://api.jquery.com/jQuery.ajax/
You could use the jquery dialog to display your message: http://jqueryui.com/dialog/
There are other ways to display a loading message. It could be as simple as using a div with a loading image (http://www.ajaxload.info/) and some text, then using jQuery to .show() and .hide() the div.
Then, in your controller, just make sure you're returning JsonResult instead of a view. Be sure to mark the Controller action with the [HttpPost] attribute.
[HttpPost]
public JsonResult TestControllerMethod(MyViewModel viewModel)
{
//do work
return Json(true);//this can be an object if you need to return more data
}
You can try creating the view to load the barebones of the page, and then issue an AJAX request to load the page data. This will enable you to show a loading wheel, or alternatively let you render the page in grey, with the main data overwriting that grey page when it comes back.
This is how we do it in our application, however there is probably a better way out there...
if not I'll post some code!
EDIT: Here's the code we use:
Controller Action Method:
[HttpGet]
public ActionResult Details()
{
ViewBag.Title = "Cash Details";
return View();
}
[HttpGet]
public async Task<PartialViewResult> _GetCashDetails()
{
CashClient srv = new CashClient();
var response = await srv.GetCashDetails();
return PartialView("_GetCashDetails", response);
}
Details View:
<div class="tabs">
<ul>
<li>Cash Enquiry</li>
</ul>
<div id="About_CashEnquiryLoading" class="DataCell_Center PaddedTB" #CSS.Hidden>
#Html.Image("ajax-loader.gif", "Loading Wheel", "loadingwheel")
</div>
<div id="About_CashEnquiryData"></div>
<a class="AutoClick" #CSS.Hidden data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#About_CashEnquiryData"
data-ajax-loading="#About_CashEnquiryLoading" data-ajax-loading-duration="10"
href="#Url.Action("_GetCashDetails", "Home")"></a>
</div>
Custom Javascript:
$(document).ready(function () {
// Fire any AutoClick items on the page
$('.AutoClick').each(function () {
$(this).click();
});
});

How to make UpdateTargetId work in Ajax.ActionLink?

I have this method in the controller
[HttpDelete]
public void DeleteDocument(int id)
{
//Here I do the deletion in the db
}
In the view I have this, calling a method that returns a partial view
#{ Html.RenderAction("GetDocumentsByMember"); }
The GetDocumentsByMember method
public ActionResult GetDocumentsByMember()
{
var companyGuid = HttpContextHelper.GetUserCompanyGuid();
var documents = _service.GetUploadedDocumentsByMember(companyGuid);
return PartialView(documents);
}
And the partial view
#model IEnumerable<GradientCapital.DomainModel.Entity.Document.Document>
<div id="uploadeddocuments">
#*Here there's a table and at one of the columns there's the next link*#
<td id="delete">
#Ajax.ActionLink("Delete", "DeleteDocument", new { id = document.Id },
new AjaxOptions
{
Confirm = "Are you sure you want to delete?",
HttpMethod = "DELETE",
OnComplete = "deleteComplete"
})
</td>
</div>
And deleteComplete just refresh everything
<script type="text/javascript">
function deleteComplete() {
window.location.reload();
}
</script>
Quite long (is correctly formatted?) code for a simple question, I can't make the ajaxoption UpdateTargetId work here instead of having to call this deleteComplete function. Any idea?
Thanks
Instead of reloading the entire page you could call the GetDocumentsByMember action using AJAX and update only the portion of the DOM that has actually changed:
<script type="text/javascript">
function deleteComplete() {
$.ajax({
url: '#Url.Action("GetDocumentsByMember")',
type: 'GET',
cache: false,
success: function(result) {
$('#uploadeddocuments').html(result);
}
});
}
</script>
Also you'd better use OnSuccess = "deleteSuccess" instead of OnComplete = "deleteComplete" because you should update only if the Delete call actually succeeded. Don't forget that the OnComplete callback is always invoked, no matter whether the AJAX call succeeded or not.

Passing parameters when submitting a form via jQuery in ASP.NET MVC

I'm trying to do a form submit to my controller through jQuery Ajax. The following code works for the most part, however, the ThreadId parameter does not get passed. If I call the controller directly without using jQuery, it gets passed, but when using jquery, I don't see the ThreadId after form.serialize(). WHat would be the easiest way to pass parameters (like ThreadId) to jQuery form post?
ASPX
<% Html.BeginForm("AddComment", "Home", new { ThreadId = Model.Id },
FormMethod.Post, new { #id = "AddComment" + Model.Id.ToString(),
#onsubmit = "javascript:AddComment(this);return false" }); %>
<%: Html.TextBox("CommentText", "", new { #class = "comment-textbox" })%>
<input id="Comment" type="submit" name="submitButton" value="Post Comment" />
<% Html.EndForm(); %>
JavaScript
AddComment = function (sender) {
var form = $(sender);
var data = form.serialize();
$.ajax({
type: "POST",
url: "/Home/AddComment",
data: data,
dataType: "html",
success: function (response) {
alert(response);
},
error: function (error) {
alert(error);
}
});
return false;
};
CONTROLLER
[HttpPost]
public ActionResult AddComment(string submitButton, Comment comment)
{
comment.CreatedDate = DateTime.Now;
comment.PosterId = LoggedInUser.Id;
_repository.AddComment(comment);
_repository.Save();
if (Request.IsAjaxRequest())
{
return View("Comment", comment);
}
else
return RedirectToAction("Index");
}
The ThreadId parameter is included in the action attribute of the form. When you are ajaxifying this form you are posting to /Home/AddComment and no longer supplying this parameter. You could do the following to ajaxify it:
$('#idofyourform').submit(function() {
$.ajax({
// use the method as defined in the <form method="POST" ...
type: this.method,
// use the action as defined in <form action="/Home/AddComment?ThreadId=123"
url: this.action,
data: $(this).serialize(),
dataType: 'html',
success: function (response) {
alert(response);
},
error: function (error) {
alert(error);
}
});
return false;
});
Another possibility is to include the ThreadId parameter inside the form as hidden field instead if putting it in the action attribute.

Categories