First day in AJAX in .NET MVC4, it doesn't work - asp.net-mvc

Still trying to figure that out, here is my workflow:
once submit been clicked, the jquery will send a post request to call the method
method return a partial view
display on <div id = "messageForm">...</div> part
below is the form view:
//SignUp.cshtml:
<div id ="messageForm">
#using (Ajax.BeginForm("SignUp", "MVP", new AjaxOptions
{
Confirm = "Are you sure you want to send this message?",
HttpMethod = "Post",                                 
InsertionMode = InsertionMode.Replace,                                 
LoadingElementId = "loading",                                 
UpdateTargetId = "messageForm"
})) {
#Html.AntiForgeryToken();
#Html.ValidationSummary(true)
<fieldset>
<legend>
messageModel
</legend>
<p>
<input type ="submit" value ="Send Message" />
</p>
</fieldset>
here is the controller:
//MVPController
[HttpPost]
public ActionResult SignUp(MVCView model){
return PartialView("_ThankYou");
}
public ActionResult SignUp(){
return View();
}
Here is the partial view in view folder:
ThankYou.cshtml:
<h1>Thank you so much! We will contact you later</h1>
When testing it, I didn't see the confirm dialog and it redirect to the thank you page
Can anyone tell me why that happened?

You have:
#using (Ajax.BeginForm("ThankYou", "MVP", new AjaxOptions
I think this should be:
#using (Ajax.BeginForm("SignUp", "MVP", new AjaxOptions
The first string is the Action name, and you only have SignUp in the controller.

Related

How can I pass a parameter together with the Model?

Here's my View (Model ActivityViewModel):
#model GPMS.Models.ActivityViewModel
<div class="tab-pane" id="managepayments" role="tabpanel">
#{ Html.RenderPartial("_Payments", Model.Payments); }
</div>
Which render a Partial (Model IEnumerable<GPMS.Models.PaymentViewModel>):
#model IEnumerable<GPMS.Models.PaymentViewModel>
#using (Ajax.BeginForm("SavePayments", "Activities", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "DynamicContainer", InsertionMode = InsertionMode.Replace, OnBegin = "AjaxBeginFormOnBegin", OnComplete = "AjaxBeginFormOnComplete", OnSuccess = "AjaxBeginFormOnSuccess", OnFailure = "AjaxBeginFormOnFailure" }))
{
#Html.AntiForgeryToken()
<!-- My Form -->
}
Which send Ajax request to my Controller's Action:
public ActionResult SavePayments(IEnumerable<PaymentViewModel> payments)
{
if (ModelState.IsValid) {
// code; here I need ActivityViewModel.ID
}
}
The question is: how can I pass to that SavePayments also my activity ID stored in ActivityViewModel.ID? Can I do with routing?
I don't want to pass the whole ActivityViewModel to SavePayments, otherwise I need to take care of its required fields for the ModelState.IsValid check.
One option is to use the overload of Html.Partial to pass the ID using additionalViewData, then retrieve it in the partial view and add it as a route value in the form.
In the main view
#{ Html.RenderPartial("_Payments", Model.Payments, new ViewDataDictionary { { "ID", Model.ID} }); }
And in the partial
#using (Ajax.BeginForm("SavePayments", "Activities", new { id = ViewData["ID"] }, new AjaxOptions { ....
Then add a parameter in the POST method for the ID
public ActionResult SavePayments(int id, IEnumerable<PaymentViewModel> payments)

PartialViewResult on submit return another partial view

I have
public PartialViewResult CodePartial(string code){
...
return PartialView("anotherpartial");
}
which on has submit button and I want that on post executed anotherpartial partialviewresult. but instead it returns this partial view inside of CodePartial view. And on debugging it's not going inside of anotherpartial action.
How can I improve that?
CodePartial.cshtml
#model Kubeti.Models.Codes
#using (Ajax.BeginForm("CodePartial", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "result", InsertionMode = InsertionMode.Replace }))
{
#Html.EditorFor(x => x.code)
#Html.ValidationMessageFor(x => x.code)
<input type="submit" value="OK" />
}
<div id="result" style="width: 500px; height:500px; border:1px solid red;">
index.cshtml
#Html.Partial("CodePartial")
#Html.Partial("anotherpartial")
Your method:
public PartialViewResult CodePartial(string code){
...
return PartialView("anotherpartial");
}
doesn't return 'to an action' as such. It simply returns the PartialView representation (meaning without the view being rendered with the layout, amongst other things) of the view that you specify.
If you want to return to another action, you need to post to that action or, alternatively, do something like this.

How to execute only one form action method in asp.net mvc view?

I have this page that contains 2 forms one exists in the layout file and the other in the view file. The first form is for newsletter subscription (an ajax form) and its location is common in the footer of the page, that's why it's in the layout and is rendered as a partial view. I have another view of the contact us page with its own form (normal form).
My issue is when I submit the contact us form, the code also goes into the action method of the subscription form and returns a model error with JsonResult causing the whole view to be rendered as text. I only want the action method of the contact us form to be executed.
Here is the subscription form in a partial view file
#model MyApp.Models.Subscriber
#using (Ajax.BeginForm("NewsletterSubscription", "Shared", null, new AjaxOptions
{
HttpMethod = "POST",
OnBegin = "OnBegin",
OnComplete = "OnComplete",
OnFailure = "OnFailure"
}, new { id = "subscribeForm" }))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(model => model.SubscriptionEmail)
#Html.ValidationMessageFor(model => model.SubscriptionEmail)
<input id="btnSubscribe" type="submit" value="Subscribe" />
}
And this is how it's rendered in the _layout.cshtml file
#{ Html.RenderAction("NewsletterSubscription", "Shared"); }
Here's the other form in contactus view file
#using (Html.BeginForm("Index", "Contact", FormMethod.Post, new { id = "contactForm" }))
{
#Html.AntiForgeryToken()
<div class="theForm">
<div class="theFormUnit">
<p>Fullname</p>
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="theFormUnit">
<p>Email</p>
#Html.TextBoxFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="theFormUnit">
<p>Phone</p>
#Html.TextBoxFor(model => model.Phone)
#Html.ValidationMessageFor(model => model.Phone)
</div>
<div class="theFormUnit">
<p>Message</p>
#Html.TextAreaFor(model => model.Message)
#Html.ValidationMessageFor(model => model.Message)
</div>
<input type="submit" value="Submit" />
</div>
}
When I debug the code, first the action method of the contact us is executed then the action method of the subscription and returns an error since the email was not provided.
The subscription action method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult NewsletterSubscription(Subscriber subscriber)
{
if (ModelState.IsValid)
{
}
else
{
return Json(new { success = false, message = "Failure Message" });
}
return Json(new { success = true, message = "Success Message"});
}
And contact us action method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(ContactViewModel contact)
{
if(ModelState.IsValid)
{
}
else
{
}
return View(contact);
}
I tried two solutions the first one partially solved the problem and the other solved it completely.
First solution was to add the following lines in the action method of the subscription form
if (!Request.IsAjaxRequest())
{
ModelState.Clear();
return PartialView("Partial/_NewsletterSubscription");
}
Here I am checking if the request is not an ajax request, which means it's the postback request for the contact us form, in this case I clear the model state to remove the error and return a new partial view. Although this solution solved the issue but I wasn't satisfied with it because I was not convinced with the fact that action method of the subscription form gets executed with the action method of the contact us form.
So later I thought of another simple solution, which totally solved the issue and the execution doesn't go into the action method of the subscription form when submitting the contact us form.
I simply changed the action method name from "NewsletterSubscription" to "Subscribe" so instead of
#using (Ajax.BeginForm("NewsletterSubscription", "Shared", null, new AjaxOptions
I changed it to
#using (Ajax.BeginForm("Subscribe", "Shared", null, new AjaxOptions

MVC4 PartialViews without returning

Main view contains #Ajax.ActionLink links that updates a div target with partialViews. All works fine.
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "divCurrentView", InsertionMode = InsertionMode.Replace })
<div id="divCurrentView"></div>
Question is: When displaying a partialview and on click currently I update data and return to the same partial view that reloads the data. also reloads dropdownlists..etc `
Calling Partial View:
public PartialViewResult MemberInfo(){
.......
ViewBag.Members= new SelectList(_db.Members, "Id", "Text");
return PartialView("_MemberInfo",model);
}
[HttpPost]
public PartialViewResult MemberInfo(InformationVM model){
....... update data
//if I dont include the ViewBag again it will fail on reload
ViewBag.Members= new SelectList(_db.Members, "Id", "Text");
return PartialView("_MemberInfo",model);
}
instead of return to the same PartialView can I just show a message on the screen "data was updated" within the partial view.?
When you define your ActionLink, you can choose the element that gets replaced:
#Ajax.ActionLink("Do something", "_Methodname", new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "StatusMessage",
InsertionMode = InsertionMode.Replace,
}, new {#class = "text-danger"})
<span id="StatusMessage"></span>
This way, the message gets loaded into the span element.
In that case, I'd include the actionlink inside the div and replace the content of the div (including the actionlink) with the partial, this time including another (different) actionlink.
<div id="divCurrentView">
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "divCurrentView", InsertionMode = InsertionMode.Replace })
</div>
Partial:
<div id="divCurrentView">
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions {
HttpMethod = "POST", UpdateTargetId = "StatusMessage", InsertionMode = InsertionMode.Replace})
<span id="StatusMessage"></span>
</div>
That way, the second (and subsequent) clicks will POST and use the different method in the controller.

ViewModel not storing values when Ajax.ActionLink calling controller

When I'm clicking ActionLink and setting ViewModel values in controller I can see changes when View being rendered. But same values comes as null into Controller when I'm clicking ActionLink second time.
How do I store the value, so it comes into controller ?
View:
#Ajax.ActionLink("Click me", "AjaxTest", "Controller", new AjaxOptions()
{
UpdateTargetId = "updatePanel",
HttpMethod = "POST",
OnSuccess = "A()"
})
<div id="updatePanel">
#Html.Partial("~/Views/Shared/_UpdatableContent.cshtml", this.Model)
</div>
Controller:
[HttpPost]
public ActionResult AjaxTest(MyViewModel model)
{
model.A = "A"
return PartialView("_UpdatableContent", model);
}
Partial view _UpdatableContent:
#Html.HiddenFor(x => x.A)
#if (Model.A == "A")
{
//Draw
}
Try adding this.Model to your ActionLink following:
#Ajax.ActionLink("Click me", "AjaxTest", "Controller", this.Model, new AjaxOptions() { UpdateTargetId = "updatePanel" })
This method passes the model back into the request, which should allow the update to happen.
Probably my biggest gripe with ASP.NET MVC is the fact that the various "Helper" functions are overloaded to the nth-degree, and not always consistently in terms of the order the arguments appear...
Hope that helps :)
I had this very same problem. Setting HttpMethod = "Post" in the AjaxOptions fixed it for me, thanks Sergejs.
My final, working code is as follows
#{
AjaxOptions ajaxOptions = new AjaxOptions
{
HttpMethod = "Post",
LoadingElementId = "product-adding-" +#Model.Product.Id,
LoadingElementDuration = 100,
OnSuccess = "AddedToCart"
};
}
<div>
#Ajax.ActionLink("Add to cart",
"AddToCart",
"Cart",
new { id = Model.Product.Id, returnUrl = Request.Url.PathAndQuery },
ajaxOptions,
new { #class = "button" })
<img id="product-adding-#Model.Product.Id" src="~/Images/ajax_loader.gif" />
</div>

Resources