Create and send whole model via AJAX in ASP.NET MVC - ajax

I am trying to implement some save functionality via AJAX. I have a view with controls defined like below to be populated from the model:
#Html.LabelFor(model => model.mediaName, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.mediaName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.mediaName, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.mediaName, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.mediaName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.mediaName, "", new { #class = "text-danger" })
When a user modifies the data and clicks the save button, the View is able to build up the new data from those controls and send it to the Controller (which simply saves the model to the db).
I am trying to do the same but via AJAX but I am having problems accessing an "already-built" model from the controls...
function saveDetails() {
var model = //HOW I GET THE MODEL?
$.ajax({
url: '/Media/SaveDetails',
dataType: 'html',
data: {
obj: model
},
success: function (data) {
}
});
}
Is there any way I can access that model? Or I should build it control-by-control?
EDIT: This is how I expect the controller to get the action:
(Medium is the entity used in the Model)
public ActionResult SaveDetails(DomainModel.Medium obj)
{
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
return PartialView("_MediaModalDetails", obj);
}

Since the form works normally before adding the AJAX, presumably you have a <form> enclosing the input elements? If so, you can serialize that form. So, for example, let's say your form has id="myForm", then you can do something like this:
var model = $('#myForm').serialize();

#using (Ajax.BeginForm("SaveDetails", "Media", new AjaxOptions { HttpMethod = "POST", OnSuccess = "AfterEntry()", OnBegin="ValidateForm()"}, new { id = "myForm" }))
It does the same thing which you will get my writing an external
$.ajax({
url: '/Media/SaveDetails',
type: "POST",
data: {obj: $('#myForm').serialize() },
success: function (data) { AfterEntry(data) }
})
// no need of dataType:'html' as its json
Using the Ajax.BeginForm technique will also do server side property validation for model.mediaName which you have mentioned in the Data Annotations
for e.g.
[Required(ErrorMessage = "Media Name is required")]
public string mediaName{ get; set; }
Using ajax.BeginForm will show error with a message if mediaName is blank..
i.e. #Html.ValidationMessageFor will be fired
you will have to write extra long validation in jquery if you are trying to do the same but via external Ajax otherwise #Html.ValidationMessageFor won't be fired

Related

Ajax.beginform does a full postback

I am using Ajax,beginform to get partial postback, have install the Microsoft.jQuery.Unobtrusive.Ajax package, set up the bundle and added the keys in config files. With all this done when i click my submit button i get full postback. Have gone though tones of post and tried all suggested solutions, still seems to not work. is there anything i am still missing.
#using (Ajax.BeginForm("MyAction", "MyController", new AjaxOptions()
{
HttpMethod = "GET",
UpdateTargetId = "divList",
InsertionMode = InsertionMode.Replace
}))
{
<button type="submit"><span><span class="sr-only">Show more List</span></span></button>
}
<div id="divList">
#Html.Partial("_MyList.cshtml", Model.List)
</div>
In my controller
public ActionResult MyAction()
{
.....
return PartialView("_MyList", list);
}
Added in the bundle
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
In web.config
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
Have even tried setting the section script in my view
#section Scripts {
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#Scripts.Render("~/bundles/jqueryval")
}
Another best way to handle the scenario is, instead of Ajax.BeginForm use Html.BeginForm
Use JQuery form submit, this will help you to stay away from jquery.unobtrusive
In .cshtml
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { id = "form_" + #Model.Id }))
{
<div>
#Html.HiddenFor(m => m.AntiForgeryToken, new { #class = "form-control" })
#Html.HiddenFor(m => m.Id, new { #class = "form-control" })
<button onclick='onClickFormButton("form_#Model.Id")' type="button">Submit</button>
</div>
}
Javascript
window.onClickFormButton = function (formId) {
//validate the form clientside
var validateResult = OnValidateForm(formId);
if (!validateResult) return false;
var form = $('#' + formId);
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
success: function (result) {
// process the result
processSuccessResult(result)
},
fail: function (result) {
// process the result
processFailResult(result)
}
});
return false;
}
Controller/Action
[HttpPost]
public JsonResult ActionName(FormCollection model)
{
// Get id
var idKey = model.AllKeys.FirstOrDefault(x => x.Contains("Id"));
var id = model[idKey];
return new ProcessedResult(id);
}

ASP MVC 3: Client Validation not working properly when submitting a form using AJAX

I have the following scenario, I have a for that I'm submitting using ajax using the following code:
$("#cmdAjaxSave").click(function (evt) {
evt.preventDefault();
var $form = $('#frmItem');
if ($form.valid()) {
ajaxSave();
}
});
function ajaxSave() {
if (!onBeforeSubmit()) return; //item is not valid, so the ajax call should not be executed
//var token = $('[name=__RequestVerificationToken]').val();
popup('ajaxSplash');
$.ajax({
type: "POST",
url: '#Url.Action("Index")',
data: $("#frmItem").serialize(),
success: function (html) {
//console.log(html);
$("#formDiv").empty();
$("#formDiv").append(html);
initItemPage();
alert("Item was saved successfully");
},
error: function () { popup('ajaxSplash'); onFailure(); }
});
}
The problem I'm seeing here is that even though "frmItem" is returning "true" when I arrive clientside the ModelState is not valid. Specifically for three properties, which actually has the correct value.
Digging into the code made by the developer who originally coded this I found that for instance this property:
#Html.TextBoxFor(model => model.Item.Service.CPC_BW, htmlAttributes: new { #class = "Text", #onkeyup = "validItem();", #id = "SrvCPCBlk" })
Is actually defined like this:
private double _CPC_BW;
[Required]
[Range(0, 100000, ErrorMessage = "CPC value required")]
public string CPC_BW { get { return String.Format("{0:F}", _CPC_BW); } set { _CPC_BW = Convert.ToDouble(value); } }
I think he did it because TextBoxFor does not offers an obvious way to format a number and even though it looks fishy I don't know how could this be causing the error.
The Html of the form is rendered like this
<div id="itemPopUpForm">
#{Html.EnableClientValidation();}
<div id="formDiv">
#{ Html.RenderPartial("ItemData", Model, new ViewDataDictionary() { { "Machines", ViewBag.Machines }, { "WarehouseList", ViewBag.WarehouseList }, { WebConstants.FORM_ID_KEY, #ViewData[WebConstants.FORM_ID_KEY] } }); }
</div>
</div>
The partial view contains the form that is submited in the ajax request.
I think you should try and clear the model state then test whether its valid...
Its a common issue.
ModelState.Clear();
ModelState.IsValid();

MVC submit form outside ajax.beginform

I have a certain ajax form and when submitted I want to include another form outside of that ajax form. Let me show you an example:
#using (Ajax.BeginForm("PayWithBankTransfer", "Payment", new { salesLineId = salesLine.SalesLineID }, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "bankForm",
LoadingElementId = "spinnerBank"
}, new { id = "feedback-form" }))
{
//some stuff
<button type="submit">Reserve</button>
}
I have another tag outside of the form I want to include in the ajax form submission
<div id="theOtherStuff">
//otherStuff
</div>
How could I submit the other stuff along with the ajax form?
I don't think that MS unobtrusive AJAX supports this. So let's get rid of it and use plain jQuery. The .serialize() method is what you are looking for.
So we start by replacing the Ajax.BeginForm form with a regular Html.BeginForm
#using (Html.BeginForm(
"PayWithBankTransfer",
"Payment",
new { salesLineId = salesLine.SalesLineID },
FormMethod.Post,
new { id = "feedback-form" })
)
{
//some stuff
<button type="submit" class="t-button t-state-default" style="width: 100px; height: 50px;">Reserver</button>
}
then we provide an id to the other form so that we can reference it in our script:
<div id="theOtherStuff">
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "theOtherStuffForm" }))
{
//otherStuff
}
</div>
and all that's left is write our script in a separate javascript file to unobtrusively AJAXify this form:
$(function() {
$('#feedback-form').submit(function () {
$('#spinnerBank').show();
$.ajax({
url: this.action,
type: this.method,
data: $(this).add('#theOtherStuffForm').serialize(),
success: function (result) {
$('#bankForm').html(result);
},
complete: function () {
$('#spinnerBank').hide();
}
});
return false;
});
});
The following line should be of particular interest:
data: $(this).add('#theOtherStuffForm').serialize(),
As you can see the .serialize method allows convert multiple forms into suitable serialized form.
It is more than obvious that you should not have conflicting names with the input elements of the 2 forms (for example have 2 elements with the same name), otherwise the default model binder could go berserk. It's up to you to resolve those conflicts if there are any.

MVC3 AJAX Cascading DropDownLists

I am having a hard time figuring out how to get cascading drop down lists to work for my asp.net mvc3 application. I have a popup box and I would like to display 2 dropdownlists, the 2nd being populated based on what is selected in the first. Each time I run the application the controller method returns the correct list of values, but instead of hitting the success part of the ajax call I hit the error part. I have done lots of research and followed several examples I have found but something is still not quite right, any help would be greatly appreciated.
Edit:
Further inspection using firebug shows an error 500 internal server error which states: Exception Details: System.InvalidOperationException: A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.GameEdition
I have the following jQuery / AJAX:
<script type="text/javascript">
$(function () {
$("#PlatformDropDownList").change(function () {
var gameId = '#Model.GameID';
var platformId = $(this).val();
// and send it as AJAX request to the newly created action
$.ajax({
url: '#Url.Action("Editions")',
type: 'GET',
data: { gameId: gameId, platformId: platformId },
cache: 'false',
success: function (result) {
$('#EditionDropDownList').empty();
// when the AJAX succeeds refresh the ddl container with
// the partial HTML returned by the PopulatePurchaseGameLists controller action
$.each(result, function (result) {
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', this.EditionID)
.text(this.EditionName)
);
});
},
error: function (result) {
alert('An Error has occurred');
}
});
});
});
Here is my controller method:
public JsonResult Editions(Guid platformId, Guid gameId)
{
//IEnumerable<GameEdition> editions = GameQuery.GetGameEditionsByGameAndGamePlatform(gameId, platformId);
var editions = ugdb.Games.Find(gameId).GameEditions.Where(e => e.PlatformID == platformId).ToArray<GameEdition>();
return Json(editions, JsonRequestBehavior.AllowGet);
}
Here is my web form html:
<div id="PurchaseGame">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true, "Please correct the errors and try again.")
<div>
<fieldset>
<legend></legend>
<p>Select the platform you would like to purchase the game for and the version of the game you would like to purchase.</p>
<div class="editor-label">
#Html.LabelFor(model => model.PlatformID, "Game Platform")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.PlatformID, new SelectList(Model.Platforms, "GamePlatformID", "GamePlatformName"), new { id = "PlatformDropDownList", name="PlatformDropDownList" })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EditionID, "Game Edition")
</div>
<div id="EditionDropDownListContainer">
#Html.DropDownListFor(model => model.EditionID, new SelectList(Model.Editions, "EditionID", "EditionName"), new { id = "EditionDropDownList", name = "EditionDropDownList" })
</div>
#Html.HiddenFor(model => model.GameID)
#Html.HiddenFor(model => model.Platforms)
<p>
<input type="submit" name="submitButton" value="Purchase Game" />
<input type="submit" name="submitButton" value="Cancel" />
</p>
</fieldset>
</div>
}
You cannot send JSON encoded requests using the GET verb. So replace type: 'GET' with type: 'POST' and it will work. Also since you have specified a JSON request you must, well, send a JSON request which is achieved with the JSON.stringify function: data: JSON.stringify({ gameId: gameId, platformId: platformId }),. But since you only have 2 values I think that using GET would be easier. So my recommendation is to remove the contentType: 'application/json' parameter and have your AJAX request look like this:
$.ajax({
url: '#Url.Action("Editions")',
type: 'GET',
data: { gameId: gameId, platformId: platformId },
cache: 'false',
success: function (result) {
$('#EditionDropDownList').empty();
// when the AJAX succeeds refresh the ddl container with
// the partial HTML returned by the PopulatePurchaseGameLists controller action
if(result.length > 0)
{
$.each(result, function (result) {
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', this.EditionID)
.text(this.EditionName)
);
});
}
else
{
$('#EditionDropDownList').append(
$('<option/>')
.attr('value', "")
.text("No edition found for this game")
);
}
},
error: function () {
alert('An Error has occured');
}
});
Also in the DropDownListFor helper in your Razor markup I notice the following:
onchange = "Model.PlatformID = this.value;"
All I can say is that this doesn't do what you might think it does.
UPDATE:
It seems that you are getting a circular object reference error because you are passing your editions domain model to the Json method. Circular reference object hierarchies cannot be JSON serialized. Besides you don't need to waste the bandwidth by sending all the crap contained in this editions to the client. All your client needs is a collection of ids and names. So simply use view models:
public ActionResult Editions(Guid platformId, Guid gameId)
{
var editions = ugdb
.Games
.Find(gameId)
.GameEditions
.Where(e => e.PlatformID == platformId)
.ToArray<GameEdition>()
.Select(x => new
{
EditionID = x.EditionID,
EditionName = x.EditionName
})
.ToArray();
return Json(editions, JsonRequestBehavior.AllowGet);
}

How can i get data in ajaxoption post request from input in razor

I would like to make POST request using following code
<div class="editor-field" id="updateDiv" >
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div>
#Ajax.ActionLink("Check Availability", "ValidateUsername", "Wizard",
new { username = "username"},
new AjaxOptions() {
UpdateTargetId = "msg",
HttpMethod = "POST",
LoadingElementId = "progress",
}
)
</div>
But instead of passing a static value I want to pass value of #Html.EditorFor(model => model.UserName) (user inputed value), how can I do this?
First, it seems you're trying to create some kind of remote validation. This mechanism is already in place in MVC so you can use it with DataAnnotations
http://msdn.microsoft.com/en-us/library/gg508808(v=vs.98).aspx
If I'm wrong with my assumption, you can try to modify your code according to the below.
I would go with adding htmlAttributes to set id for the link like this :
#Ajax.ActionLink("Check Availability", "ValidateUsername", "Wizard",
new {username = "username"},
new AjaxOptions()
{
UpdateTargetId = "msg",
HttpMethod = "POST",
LoadingElementId = "progress",
}, new { id = "CheckAvailabilityLink" }
)
having that in place we have a direct reference to this element so we can track changes on the textbox and update URL dynamically
<script type="text/javascript">
$(document).ready(function () {
$("#UserName").keyup(function (e) { // textbox id here
var href = $("#CheckAvailabilityLink").attr("href").split("?", 1);
$("#CheckAvailabilityLink").attr("href", href + "?username=" + $(this).val());
alert($("#CheckAvailabilityLink").attr("href"));
});
});
</script>
</script>

Resources