ASP.NET MVC: Multiple submit buttons using Ajax.BeginForm - asp.net-mvc

I want to create a page that has a next button and previous button that switches the image displayed.
For that purpose I created an Ajax.BeginForm and inserted into it, an image and two submit buttons.
Can I (should I) have multiple submit buttons inside an Ajax.BeginForm?
How would the controller handle each submit separately?

Try this,
View
#model TwoModelInSinglePageModel.RegisterModel
#using (Ajax.BeginForm("DYmanicControllerPage", "Test", FormMethod.Post,null, new { id = "frmSignUp" }))
{
<div>
<input type="hidden" id="" name="hidden2" id="hdasd" />
#Html.HiddenFor(m => m.hidden1)
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</div>
<br />
<div>
#Html.LabelFor(m => m.Address)
#Html.TextBoxFor(m => m.Address)
#Html.ValidationMessageFor(m => m.Address)
</div>
<br />
<div>
#Html.LabelFor(m => m.PhoneNo)
#Html.TextBoxFor(m => m.PhoneNo)
#Html.ValidationMessageFor(m => m.PhoneNo)
</div>
<input type="submit" value="Save" id="btnSave" name="ButtonType"/>
<input type="submit" value="Next" id="btnNext" name="ButtonType" />
}
Controller
[HttpPost]
public ActionResult DYmanicControllerPage(RegisterModel model, string ButtonType)
{
if(ButtonType == "Next")
{
// Do Next Here
}
if (ButtonType == "Save")
{
//Do save here
}
return JavaScript("REturn anything()");
}

I would recommend that you have two buttons and then depending on what button was clicked you could set the action on the form:
Razor
$(function (){
$("#btn-prev").click(function() {
$("#form").attr
(
"action",
"#Url.Action("Action", "Controller", new {area="Area" })",
).submit();
});
$("#btn-next").click(function() {
$("#form").attr
(
"action",
"#Url.Action("Action", "Controller", new {area="Area" })",
).submit();
});
});
I am using jQuery here to do this, but I think you can get the idea.

I had the same requirement/issue and tried both solutions here and they both work for me. I LIKE the idea of setting the action via jquery when clicking so I can keep my actions separate so they can be used by other views.
HOWEVER, I've found that when I do this while I debug, it posts TWICE and BOTH the OnSuccess and OnFailure are triggered. It only happens when debugging though. Keep this in mind when picking.

Related

MVC parial view going to get method rather than post on submit

I have a partial view which shows a search box with a couple of options
model PatientSearchViewModel
#using (Html.BeginForm("Index", "Patient", FormMethod.Post, new { #class = "form-inline" }))
{
<form class="form-inline">
<label style="padding-right:20px;">Search for a patient</label>
#Html.EditorFor(model => model.SearchText, new { htmlAttributes = new { #class = "form-control", placeholder = "Search...", autocomplete = "off" } })
<div class="checkbox">
<div class="checkbox" style="padding-left:20px; padding-right:20px;">
<label>#Html.EditorFor(model => model.ShowOnlyOpenClients) Show only open clients</label>
</div>
</div>
<button type='submit' name='seach' id='search-btn' class="btn btn-primary"><i class="fa fa-search"></i>Search</button>
</form>
}
When the submit button is hit I want it to go to my method below with the search content already complete, however instead it goes to the HttpGet method which is bringing back a null for the PatientSearchViewModel.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(PatientSearchViewModel patient)
{
PatientSearchViewModel patientVM = GetSearchResults(patient);
return View(patientVM);
}
Can anyone explain how I can get this to work?
I have executed the code snippet you pasted here and found that
As you are making the use of the [ValidateAntiForgeryToken]
attribute so you need to add the #Html.AntiForgeryToken(); this
statement in your partial view inside the
#using(Html.BeginForm()){ } block.
One more thing make sure that your action method will be inside the
PatientController.
For your reference (this is my code)
#model WebApplication.Models.PatientSearchViewModel
#using (Html.BeginForm("Index", "Patient", FormMethod.Post, new { #class = "form-inline" }))
{
#Html.AntiForgeryToken();
<label style="padding-right:20px;">Search for a patient</label>
#Html.EditorFor(model => model.SearchText, new { htmlAttributes = new { #class = "form-control", placeholder = "Search...", autocomplete = "off" } })
<div class="checkbox">
<div class="checkbox" style="padding-left:20px; padding-right:20px;">
<label>#Html.EditorFor(model => model.ShowOnlyOpenClients) Show only open clients</label>
</div>
</div>
<button type='submit' name='seach' id='search-btn' class="btn btn-primary"><i class="fa fa-search"></i>Search</button>
}
Suggestion: As you have used the #using(Html.BeginForm()) which will any way going to convert to <form> tag so instead of using another <form class="form-inline"> inside it is creating the confusion please you may remove it.
I hope this will solve your problem.

Partial view doesn't load inside current view

So I have this partial view:
#model QueryQuestionManager.Models.Domain.Answer
<script src="~/Scripts/DynamicList.js"></script>
<div class="editorRow">
#using (Html.BeginCollectionItem("Answers"))
{
<div>
#Html.LabelFor(x => x.AnswerText)
#Html.EditorFor(x => x.AnswerText)
</div>
delete
}
</div>
I want to load this partial view in my main view when someone clicks the link:
#model QueryQuestionManager.Models.Domain.Question
#{
ViewBag.Title = "Create";
}
<script src="~/Scripts/DynamicList.js"></script>
<br />
#using (Html.BeginForm(new { #class = "form", role = "form" }))
{
<div class="form-group">
<div class="editor-label">
#Html.LabelFor(m => m.QuestionText)
</div>
<div class="form-control">
#Html.TextBoxFor(m => m.QuestionText)
#Html.ValidationMessageFor(m => m.QuestionText)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Category)
</div>
<div class="form-control">
#Html.TextBoxFor(m => m.Category)
#Html.ValidationMessageFor(m => m.Category)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Answers)
</div>
<br />
<div id="editorRows">
#foreach (var item in Model.Answers)
{
#Html.Partial("EditorRow", item);
}
</div>
#Html.ActionLink("Add another...", "BlankEditorRow", "Question", new { id = "addItem" })
<br />
<input type="submit" value="Save" class="btn btn-success" />
</div>
}
<br />
<div>
#Html.ActionLink("Back to List", "Index", "Home")
</div>
This is the action result from the controller
public ActionResult BlankEditorRow()
{
return PartialView("EditorRow", new Answer());
}
And then I have a javascript file for the dynamic adding and deleting partial views.
$('#addItem').click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) {
$('#editorRows').append(html);
}
});
return false;
});
$('a.deleteRow').live('click', function () {
$(this).parents('div.editorRow:first').remove();
return false;
});
But for some reason my partial view doesn't load inside my current view and it opens only the partial view.
Am I forgetting something?
I'm thinking you don't want that action link. I believe your jQuery click handler has the correct approach, but your action link is linking to your new URL (Question/BlankEditorRow) and you don't want that. Rather you just need to fetch this html, and insert it as you do, but WITHOUT navigating the main page to that URL.
Instead of the ActionLink, use an anchor tag and a hidden field.
<a id="addItem" href="#" />
<input type="hidden" id="partialViewAction" value="#Url.Action("BlankEditorRow","ControllerName")"
Now in your ajax call, instead of using the href property of the anchor, use the value of the hidden field.
url: $('#partialViewAction').val()

Get changed item from DropDownListFor into another form

I have 3 forms each has access to its own Delete/Create/Edit action on server side.
When I change the DropDownListFor selected item and do a Delete then the string title is passed to the server
When I change the DropDownListFor selected item and do a Create/Edit then the string title is not passed to the server.
How can I let my Create/Edit form know of my change in the DropDownListFor ?
Passing the initial title value works with the Create/Edit action. So the problem is the change event.
index.cshtml
#using (Html.BeginForm(MVC.Configuration.Addresses.ActionNames.Delete, MVC.Configuration.Addresses.Name, new { #area = MVC.Configuration.Name }, FormMethod.Post, HtmlAttributes.Form))
{
<div class="form-group required">
#Html.LabelFor(m => m.Title, HtmlAttributes.Label)
<div class="col-md-6">
#(Html.Kendo().DropDownListFor(m => m.Title)
.BindTo(Model.Addresses.OrderBy(order => order.Text))
.HtmlAttributes(HtmlAttributes.KendoControl))
</div>
</div>
<input type="submit" value="Delete" class="btn btn-default" />
}
#using (Html.BeginForm(MVC.Configuration.Addresses.ActionNames.Edit, MVC.Configuration.Addresses.Name, new { #area = MVC.Configuration.Name }, FormMethod.Get, HtmlAttributes.Form))
{
#Html.HiddenFor(p => p.Title)
<input type="submit" value="Edit" class="btn btn-default" />
}
#using (Html.BeginForm(MVC.Configuration.Addresses.ActionNames.Add, MVC.Configuration.Addresses.Name, new { #area = MVC.Configuration.Name }, FormMethod.Get, HtmlAttributes.Form))
{
#Html.HiddenFor(p => p.Title)
<input type="submit" value="Add" class="btn btn-default" />
}
you can use 1 form with multiple buttons, you will achieve the desired result and your view will be cleaner.
As far as I know there are two main methods:
1 MVC Action: You can check the value of clicked submit button in MVC action and then perform the desired things. Example given that your buttons have as name "submitButtonName".
[HttpPost]
public ActionResult YourAction(string submitButtonName, YourFormModel model)
{
switch (submitButtonName) {
case "create":
CreateMethod(model);
break;
case "edit":
EditMethod(model);
break;
case "delete":
DeleteMethod(model);
break;
}
}
3 MVC Actions: You can change the form target action on button click using javascript. Example given that your buttons have as class "submitButtonClass".
$(".submitButtonClass").on("click", function(e){
e.preventDefault();
$('#yourFormId').attr('action', $(this).val()).submit();
});
I wrote the code quickly without testing it but it should work :)
Have a nice day,
Alberto

Change method call order when form is posted in MVC

a bit of a strange one here. I've got a blog that's another 'dip my toe into MVC' project that will highlight the benefits of our new Intranet. I have a typical blog post page with the article, comments and a form to submit your comments against a given article.
I have a stringly typed main View ("Post") which has the article content, then two Html.RenderAction methods; one to get the comments and one to add comments via a form.
My issue is that when a comment is posted, my GetComments method is called before AddComment, so when the page is refreshed the new comment isn't visible although it has been added to the database. A quick F5 confirms this. I understand that GetComments is being called first due to it being declared first in the view but I'm not sure how to tell the view to do an add before a get.
Here's my code:
Controller:
public ActionResult AddComment()
{
return PartialView("AddComment");
}
[HttpPost]
public ActionResult AddComment(Comment comment)
{
comment.DateSubmitted = DateTime.Now;
db.Comments.Add(comment);
db.SaveChanges();
return PartialView(comment);
}
public ActionResult GetComments(int articleid)
{
var comments = db.Comments.Where(c => c.ArticleID == articleid).ToList();
return PartialView(comments);
}
Post view
#model IntranetBlog.Models.Article
#{
ViewBag.Title = "Post";
}
<div class="row">
<div class="span12">
<h3>#Html.DisplayFor(modelItem => Model.Title)</h3>
<small>by Ruth Barlow on #Html.DisplayFor(modelItem => Model.DateCreated)</small>
#if (Model.Image != null)
{
<p>
<img src="#Url.Action("GetImage", "Home", new { articleID = Model.ArticleID })" alt="" width="150" height="150" />
</p>
}
<div>
#Html.DisplayFor(modelItem => Model.Body)
</div>
<small>Posted under #Html.DisplayFor(modelItem => Model.Category.Name)</small>
</div>
<div class="span12">
#{
Html.RenderAction("GetComments", "Home", new { articleID = Model.ArticleID });
}
</div>
<div class="span12">
#{
Html.RenderAction("AddComment", "Home", new { articleID = Model.ArticleID });
}
</div>
</div>
GetComments partial:
#model IEnumerable<IntranetBlog.Models.Comment>
#if (Model.Any())
{
<h3>What you're saying</h3>
foreach (var item in Model)
{
<div>
Comment: #Html.DisplayFor(modelItem => item.Body)
</div>
<div>
Submitted by: #Html.DisplayFor(modelItem => item.SubmittedBy)
on #Html.DisplayFor(modelItem => item.DateSubmitted)
</div>
<hr />
}
}
else
{
<p>There are no comments for this post. Why not add one?</p>
}
AddComment partial
#model IntranetBlog.Models.Comment
#using (Html.BeginForm())
{
<h3>Why not leave us a comment?</h3>
#Html.ValidationSummary()
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Body, 20, 20, null)
#Html.ValidationMessageFor(model => model.Body)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubmittedBy)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SubmittedBy)
#Html.ValidationMessageFor(model => model.SubmittedBy)
</div>
<p>
<input type="submit" value="Add comment" id="AddComment" class="btn btn- primary" />
</p>
</fieldset>
}
Hope this makes sense.
Thanks in advance.
The trick is to use Html.Action instead of Html.RenderAction this will allow you to store the result in a variable and then add it to the display where it is needed.
This will allow you to generate the PartialView in the logical order you need, while displaying them in an other order.
See this post for a quick example on how to do it : https://stackoverflow.com/a/13301057/971693
I would suggest to you a slightly different approach. Instead of using Html.BeginForm, think about using Ajax.BeginForm to submit the comment. Being ajax, it will have better performance as it allows your method to just return the comments so you can replace the old ones, or even just the newly added one so it can be added to the bottom of the list. Having said that, this solution does rely on the use of javascript and preferably jquery-unobtrusive-ajax to work and render decent looking client code. You don't have to know javascript to use this method thanks to the AjaxOptions class which has some powerful yet easy to use options built into it.

javascript validate not working in mvc app

In my MVC4 app, I'm trying to create a client side validator for some textboxes in a form. The form is contained in the Register.cshtml view (this view is automatically created by VS2010 when i make a new MVC4 with formsauthentication project).
The form already implements validation on Submit button, but I want a validator that acts as soon as focus leaves the textbox - should show me an inline error just next to the textbox, with a red font. if it validated ok, it should again show the message 'ok'.
The problem: The .js validator doesn't work. Firebug says:
$document is not defined.
$document.ready(
I also tried alert(), if i place it outside the document.ready function, it pops. when inside, it doesnt pop.
Code for registerForm.js:
$document.ready(
function ()
{
$('#registration form').validate
(
{
rules:
{
UserName: { required: true }
},
success: function (label)
{
label.Text('OK!').addClass('IsInputValid');
}
}
)
}
);
The bundle that adds this javascript is as follows:
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/registerForm.js",
"~/Scripts/jquery.validate*"));
Finally, the view:
<div id="registration">
#using (Html.BeginForm()) {
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
What could prevent the js from working?
I've already spent about 5hours trying to figure this out. Please let me know if you have any suggestions,
Regards,
#Html.TextBoxFor(m => m.UserName, new { #class = "required" })
No need for JS here. And what everyone else is saying about $document not being the correct syntax, unless you have var $document = $(document) defined somewhere.
You should use jQuery wrapper for document object:
$(document).ready(function () {
// your function
});
Is $document defined? jQuery defines a $ object. You can use that object as a function and pass it a selector, including document, and call .ready() on that:
$(document).ready(someFunction);

Resources