Form not 'POST'ing with a FormMethod.Post attribute - asp.net-mvc

I have a form in a view like so:
#using (Html.BeginForm("CreateDTActionBasedOnSelectedMetaAction", "TestCase", FormMethod.Post))
And an action method with the following signature:
[AcceptVerbs( new string[]{"GET","POST"})]
public void CreateDTActionBasedOnSelectedMetaAction(FormCollection fc)
However, when the 'submit' button (located in the form) is clicked, it comes to the action method, but the Request.HttpMethod property shows a "GET", and obviously the form data is then not available in the FormCollection object as it wasn't posted.
Any thoughts?
UPDATE:part of the View:
#using (Html.BeginForm("CreateDTActionBasedOnSelectedMetaAction", "TestCase", FormMethod.Post)){
<fieldset>
<legend>Test Case</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
<p>#DTContext.CurrentTestCase.Name</p>
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Criteria)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Criteria)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DTTestCaseReqSet.DTMetaReqProcessor.DTMetaActions)
</div>
<div class="editor-field">
#Html.ListBox("MyListBox", new SelectList(Model.DTTestCaseReqSet.DTMetaReqProcessor.DTMetaActions.Where(p => p.Enabled == true), "NameWithID", "NameWithID"));
</div>
<p>
<input type="submit" value="Select" />
</p>
</fieldset>
}
UPDATE2:
Okay that was silly. Turns out that the app has a custom routing system written by another dev, which expected a certain parameter in the query string to be preserved, which my code wasn't doing. This resulted in the routing system taking the POST from the form, being unable to find a suitable method, it converted it to a GET, which then found my actionmethod.
I would request this question to be deleted.

Create two action methods. One for get and one for post.
[HttpPost]
public void CreateDTActionBasedOnSelectedMetaAction(FormCollection fc)
[HttpGet]
public void CreateDTActionBasedOnSelectedMetaAction()

Related

How to get a form value in another form value submission

I had username and password in first form and retrieve password in another form , in which i want userame entered in first form in submission of second form.
#model Network.Models.LoginDetail
<script type="text/javascript">
</script>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<p>
Please enter your user name and password. #Html.ActionLink("Register", "Register") if you don't have an account.
</p>
#using (Html.BeginForm("Index","Student",FormMethod.Post)) {
<div>
<fieldset>
<legend>Account Information</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Username)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Username)
#Html.ValidationMessageFor(model => model.Username)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Log in" style="width: 200px"/>
</p>
#ViewBag.Message
</fieldset>
</div>
}
#using (Html.BeginForm("RetrievePassword","Student",FormMethod.Post)) {
<p>
Forgot password? <input type="submit" onclick="updateUsername();"value="Click Here" style="width: 150px"/> Entering your username to retrieve your password.
</p>
#Html.HiddenFor(model => model.Username)
}
How can we acheive this, i tried in javascript capturing of username in hiddenfield but not working.
Or can form within form be used to acheive this.
Please provide a solution.
Thanks,
Michaeld
At the post method of "Index" action in "Stundent" controller you will be able to get the value either in formcollection or in LoginDetail object form.
Example:
public ActionRestult Index(FormCollection FC)
{
string UserName = FC["Username"];
string Password = FC["Password"];
}
Or Alternatively
public ActionRestult Index(LoginDetail obj)
{
string UserName = obj.Username;
string Password = obj.Password;
}
This will fix your concern.

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.

MVC - Editing a list of objects

I have the following class layout in MVC:
public class ReportModel
{
List<SomeItem> items;
string value;
string anotherValue;
}
now I create a strongly typed view in MVC of this type and make editable text fields to edit each value as well as use a foreach loop to populate text fields to edit the items in the list of someitem.
when I submit to the httppost method the singular values come back fine in the reportmodel object but the list does not get returned in the object. How should this be done?
When I say httppost I am referring to the method that MVC is posting back to
[HttpPost]
public ActionResult EditReport(ReportModel report)
{
// Save the report in here after the update on the UI side
}
View code for posting the list of someitem
if (Model.items != null && Model.items.Count > 0)
{
for (int i = 0; i < Model.items.Count; i++)
{
<div class="editrow">
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items.ElementAt(i).propertyOne)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items.ElementAt(i).propertyOne)
#Html.ValidationMessageFor(m => m.items.ElementAt(i).propertyOne)
</div>
</div>
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items.ElementAt(i).propertyTwo)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items.ElementAt(i).propertyTwo)
#Html.ValidationMessageFor(m => m.items.ElementAt(i).propertyTwo)
</div>
</div>
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items.ElementAt(i).propertyThree)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items.ElementAt(i).propertyThree)
#Html.ValidationMessageFor(m => m.items.ElementAt(i).propertyThree)
</div>
</div>
</div>
}
}
Don't use ElementAt(1) in your lambda expressions => this ruins your input field names. Please read the blog post that Kirill suggested you.
So you could use indexed access:
for (int i = 0; i < Model.items.Count; i++)
{
<div class="editrow">
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items[i].propertyOne)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items[i].propertyOne)
#Html.ValidationMessageFor(m => m.items[i].propertyOne)
</div>
</div>
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items[i].propertyTwo)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items[i].propertyTwo)
#Html.ValidationMessageFor(m => m.items[i].propertyTwo)
</div>
</div>
<div class="edititem">
<div class="editor-label">
#Html.LabelFor(m => m.items[i].propertyThree)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.items[i].propertyThree)
#Html.ValidationMessageFor(m => m.items[i].propertyThree)
</div>
</div>
</div>
}
Of course in order to have indexer access to the collection this assumes that your items property is declared as either List<SomeItem> or SomeItem[]. If it is an IEnumerable<SomeItem> it won't work. So simply change the type of this property on your view model.
Kirill's reference to Scott Hanselman's blog entry is correct, but you're reading it too narrowly. In the example shown, he passes the array to the action method, but it could just as easily be contained within the parent model as well. The same concept applies.
However, one thing to know is that the default model binder does not instantiate nested classes, so it will not create an instance of the List class, which means it will always be null. To fix this, you must instantiate the empty list class in the constructor.
This is only part of the problem, though as the data must be formatted in the correct way for the model binder to bind it. This is where Scott's blog post comes in, as it provides the format needed for the model binder to recognize the data as a list.
This is typically handled for you if you use an EditorTemplate and use Html.EditorFor(m => m.Items) and then have a SomeItem.cshtml EditorTemplate. This deals with the issues of collection item naming (so long as you also use strongly typed helpers in the template as well).

Html.RenderAction in MVC with EntityFramework

My problem is when I try to render a view with two Html.RenderAction. It says: "The operation cannot be completed because the DbContext has been disposed".
I have Ninject configured in this way:
Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope;
But If I do in the default way...
Bind<IUnitOfWork>().To<UnitOfWork>()
there's no error.
I have to work with it in RequestScope (so I think), but how can I do it? It seems's that when second Html.RenderAction is called the previous DbContext it's disposed !
UPDATED:
This is the main view (summarized for brevity)
#model FoodAway.Model.Product
#Html.ValidationSummary(true)
<fieldset>
<legend>Producto</legend>
#using (Html.BeginForm())
{
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
#Html.HiddenFor(model => model.Id)
<p>
<input type="submit" value="Guardar" />
</p>
}
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Ingredients)
</div>
<div class="editor-field">
#{Html.RenderAction("IngredientsToRemoveList", "Ingredients");}
</div>
</fieldset>
<fieldset>
#{Html.RenderAction("IngredientsToAddList", "Ingredients");}
</fieldset>
</fieldset>
and his controller/action:
public ActionResult EditProduct(string name)
{
Product product = unitOfWork.ProductRepository.Get(i => i.Name ==name).FirstOrDefault();
if (product == null)
return HttpNotFound();
return View(product);
}
So, the error in DBContext is when I have this 2 RenderAction methods, the strange thing is if I have only 1 RenderAction there is no problem!!!!!
You need to enumerate the set before passing it to the view. This means that you query the database within the valid scope of the DbContext.
You can do this by using .ToArray() in your controller

Upload file along with text in Create view

My first .Net/MVC project, I generated a view that allows me to list, edit, and create items in a database, and I would like to add a file upload control to the Create page, to just upload one file.
I understand that within [HttpPost] I need "public ActionResult Index(HttpPostedFileBase file)" but my current [HttpPost] is like this: "public ActionResult Create(lm_pics lm_pics)".
Why do you have 2 file inputs? Why do you have 2 forms? Isn't the first form sufficient (assuming of course you add the enctype="multipart/form-data" attribute to it as you cannot upload files without it)?:
#model PictureUploader_MVC.Models.lm_pics
#{
ViewBag.Title = "Create";
}
<h2>Upload Picture</h2>
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>lmit_pics</legend>
<div class="editor-label">
#Html.LabelFor(model => model.brand)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.brand)
#Html.ValidationMessageFor(model => model.brand)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.enabled)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.enabled)
#Html.ValidationMessageFor(model => model.enabled)
</div>
<div>
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
</div>
</fieldset>
<button type="submit">Create</button>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
and your controller action that will process the form submission:
[HttpPost]
public ActionResult Create(lm_pics lm_pics, HttpPostedFileBase file)
{
...
}

Resources