Html.BeginForm inside of Html.BeginForm MVC3 - html

I have a main view that renders two partial views. The main view encompasses both of the partial views within a form. Each of the partial views also contain forms. All 3 views share the same viewmodel. What I want to do is encapsulate the data from all views with the main view, and run specific Controller action results with the partial views.
I want to know if this is even possible. When debugging I see that my content always posts to the HTTPPost of the Main views form. I have submit buttons for each of the forms accordingly. Sorry for the code post, its coming out all split up.
Main View:
#using (Html.BeginForm("Main", "Registration", FormMethod.Post,
new { #class="mainform" }))
{
<form>
<fieldset>
<div id ="option1" class="conglomerate">
#Html.Partial("_GetBusiness")
</div>
<div id ="option2" class="dealership">
#Html.Partial("_GetLocation")
</div>
<button type="submit" value="Create" class="buttonBlue" id="">
<span>Create a new dealer</span>
</button>
</fieldset>
</form>
Partial 1
#using (Html.BeginForm("CreateBusiness", "Business", FormMethod.Post,
new { #class="buisinessform" }))
{
<form>
<div class="editor-field">
#Html.DropDownListFor(m =>m.BusinessId, new SelectList(Model.Businesses,
"BusinessId", "BusinessName"), "")
</div>
<label>Your company not listed? Register yours below:</label>
<div class="editor-label">
#Html.LabelFor(model => model.BusinessName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.BusinessName)
#Html.ValidationMessageFor(model => model.BusinessName)
</div>
<button type="Button" value="" class="buttonBlue"id="TestSubmit">
<span>Add Dealer</span>
</button>
<div class ="confirm">
<button type="submit" value="Create" class="buttonBlue" id="">
<span>Click here to confirm dealer addition</span>
</button>
</div>
</form>
}

As Dave mentions, It is not valid HTML to nest forms. It's not just HTML5, but any version of HTML.
It might work in some browsers, in certain circumstances, but it is never valid. And you can never depend on what will happen even if it does seem to work. Your best course of action is to use multiple non-nested forms on a page.
Can you explain why you think you need nested forms?

No, you cannot have nested forms. Sorry.
See HTML5 guidelines
"Flow content, but with no form element descendants"

Related

How to correctly iterate elements?

I'm iterating my Model.Payments collection (which is an public IEnumerable<Payments> Payments { get; set; }):
#using (Html.BeginForm())
{
<div class="payments">
#foreach (var payment in Model.Payments)
{
Html.RenderPartial("_Payment", payment);
}
</div>
<input type="submit" value="Aggiorna" />
}
And this is my _Payment partial:
#model MyProject.Models.Payments
<div class="payment-row">
<span>ActivityID:</span> #Html.TextBoxFor(x => x.ActivityID)
<span>PaymentType:</span> #Html.TextBoxFor(x => x.PaymentType)
<span>Amount:</span> #Html.TextBoxFor(x => x.Amount)
</div>
But it doesn't create a proper HTML. Name/ID are the same, so once I postback, I can't retrieve data.
Where am I wrong?
The correct approach is to use the EditorFor() method, which will correctly prefix your inputs with the collection indexer
Rename you partial view to Payments.cshtml (to match the name of the class), and move it to the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTempatesFolder)
Then you view becomes
#using (Html.BeginForm())
{
<div class="payments">
#Html.EditorFor(m => m.Payments)
</div>
<input type="submit" value="Aggiorna" />
}
The EditorFor() method generates the correct html for each item in the collection, and will generate inputs such as <input name="Payments[0].ActivityID" ... /> rather than <input name="ActivityID" ... /> which you are currently generating.
As a side note, you should consider using #Html.LabelFor() to generate a <label> associated with your form controls, rather than using a <span>.

taking input from button in thymeleaf

I am displaying a form to user with some details and then user clicks approve and reject. In the backend, I want to take this in one property - userAction, which can be "approve" or "reject".
How can I add input to the buttons that user clicks? And then this input is part of the object requestDto.
<form th:action="#{/mission/store/{uuid}(uuid=${uuid})}" th:object="${requestDto}" method="post" class="mission_form">
<div class="wizard-header">
<h3 class="wizard-title">
Approve Mission
</h3>
<h5>Should you chose to accept this mission, press approve.</h5>
</div>
<div class="wizard-footer">
<!--<div class="pull-right">-->
<input type='button' class="btn btn-success" name='approve' value='Approve' />
<!--</div>-->
<div class="pull-left">
<input type='button' th:field="*{}" class='btn btn-danger' name='previous' value='Decline' />
</div>
<div class="clearfix"></div>
</div>
</form>
First define a proper DataTransferObject:
public class MyRequestDto {
private String userAction;
// don't forget getters and setters
}
Then add a object of that class to your model
// if you are return a M&V-object:
ModelAndView mv = new ModelAndView("viewName")
ModelAndView.addObject("requestDto", new MyRequestDto());
// if you define a Model-Object as input-parameter:
Model.addAttribute("requestDto", new MyRequestDto());
Define a form like this (I used button-elements). The point is not to use the th:field attribute:
<form th:action="...." method="POST" th:object="${requestDto}">
<button name="userAction" value="approve" >Approve</button>
<button name="userAction" value="reject" >Reject</button>
</form>
Recieve the DataTransferObject by adding this to your controllers input-paramters (the controller that handels the post request):
... #Valid RequestDto requestDto, BindingResult bindingResult, ....
Now you can access requestDto's userAction-Attribute. The value is approve if you click the first button and it is reject if you click the second button. First you can check if there are binding errors by checking bindingResults.hasErrors().

ASP.NET MVC Redirect to same page after submit button is clicked

some friends and I have started a team project but we are stuck at this point. We made a function of the site to add comments under every post. The problem is that when submit is clicked it adds the comment but doesn't refresh the page which causes some problems. The author stays "anonymous" and when refresh button is clicked it shows an alert:
The page that you're looking for used information that you entered. Returning to that page might cause any action you took to be repeated. Do you wish to continue?
However, if we just type the URL again and click ENTER everything is good: the author shows up and the comment appears only once. So the only solution for us is to redirect to the same page. Here is the form:
<div style="text-align: left">
<div><i>Leave your comment</i>
</div>
<form method="post">
<fieldset>
<p>
<textarea rows="10" class="form-control" type="text" id="1" name="commentText" style="height: 100px"></textarea>
</p>
<p>
<input type="submit" name="buttonSubmit" onclick="redirect" value="Add comment" class="btn btn-default" style="" />
</p>
</fieldset>
</form>
</div>
This is how the comments are printed on the page:
<h3>Comments</h3>
<br>
<div>
#foreach (var comment in ViewBag.Comments) {
<section class="row">
<article class="post col-md-12">
<div class="about">
Posted on <i>#comment.Date</i>
#if (comment.AuthorId != null) { #: by <i>#comment.Author.FullName</i>
}else { #: by <i>anonymous</i>
}
</div>
<div class="body">#comment.Text</div>
</article>
</section>
}
</div>
Thanks in advance!
Use the #Ajax.BeginForm() helper.
In the AjaxOptions(), you'll want to set the Httpmethod to "Post", InsertionMode.InsertBefore (or After depending on where you want new comments), and UpdateTargetId = "your-comment-div".
You'll need to include jquery.unobtrusive-ajax.js so the Ajax helper works correctly. You can use the nuget package manager to pull it in. Be sure to add it to your jquery bundle.
Then, you need to create the post action in your controller to accept the comment params and return a partial view.
Doing thIs keeps the Ajax confined to a razor helper and controller method.
View:
#using (Ajax.BeginForm("AjaxAddComment",
"MyController",
/* route params */,
new AjaxOptions()
{
InsertionMode = InsertionMode.InsertBefore,
UpdateTargetId = "commentsDiv",
HttpMethod = "Post"
}, htmlAttributes: new { /* ... */ }))
{
<div class="form-horizontal">
#* comment form... *#
</div>
}
...
<div id="commentsDiv">
<div class="comment">...</div>
<div class="comment">...</div>
</div>
Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public async Task<PartialView> AjaxAddComment(string comment, /*...other params*/)
{
var newComment = ... // comment viewModel
// add comment to db
await db.SaveChangesAsync();
return PartialView("_CommentPartial", newComment);
}
_CommentPartial:
<div class="comment">
...
</div>

How to post to 2 separate forms in different tab panels

My ASP.NET MVC application includes a Razor view with 3 tabs with a bunch of fields.
tab 1-2 has a bunch of fields
tab 3 should have a file upload section which should be rendered from a partial view.
Here is the structure:
<body>
<div class="container well" >
#using (Html.BeginForm(new { Action = "action X",id="publishingForm" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-horizontal">
<div class="tabbable">
<ul class="nav nav-tabs">
<li class="active"><span><strong>Step 1:</strong></span> Enter required fields</li>
<li><strong>Step 2:</strong> Enter optional fields</li>
<li><strong>Step 3:</strong> Upload your video</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="requiredtab">
</div>
<div class="tab-pane" id="optionaltab">
</div>
<div class="tab-pane" id="uplaodtab">
partial view here
</div>
</div>
</div>
</div>
}
</div>
</body>
</html>
As you can see the tab Divs are wrapped in a form (post to Controller X).
The problem is the partial view for the file upload also includes a form which should post to a handler. But when I initiate a file upload in Tab 3 it is attempting to post to Controller X of the original form.
How can I structure my view to have the fields included in the form on tabs 1 & 2 post to Controller X and the Form in Tab 3 post to its correct handler?
The bottom line is I am trying to post separately to 2 different forms included in different tab panels
i suggest that you wrap your two forms with Ajax Forms, and you can submit them both asynchronously
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "updateDiv" }))
{
<input type="submit" id="submitForm1" value="OK" />
}
<div id="updateDiv"></div>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "updateDiv2" }))
{
<input type="submit" id="submitForm2" value="OK" />
}
<div id="updateDiv2"></div>
and call the .click() for each submit button whenever your code needs to.

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..