It is hard for me to clearly state the problem I am having. I am trying to understand how to retain values in form fields created in a loop after validation fails. I have a more complicated real world form that has a bunch of elements created in the loop and validation. I have reduced it to a simple example included below.
When validation fails I would like the textareas named "Comment" that have been created in the loop to retain the values that are shown in the Pre-Submit image below.
When I debug the form submission, the values from each of the fields are successfully connected to the IList variable named Comment found in the Model. This is what I want so I can loop through and locate them based on index.
After submitting, each textarea produced by the loop shows the comma separated representation of the IList variable Comment in the Model. It appears that the field in the view and in the model are connecting because they share a name. They connect properly on the way in but not on the way out. I would like the view to only show the value associated with the Comment[i] instead of the entire list so that the values remain constant between form submissions.
Screenshots and Sample Code Below
First Load:
Pre-Submit Form Changes:
Form as seen after first submit:
Form as seen after second submit:
Model Code
using System.Collections.Generic;
namespace UI.Models.Forms
{
public class TempListModel : ContentModel
{
public TempListModel()
{
Comment = new List<string>();
}
public IList<string> Comment { get; set; } //Comments for each URL in the list
}
}
View Code
@model UI.Models.Forms.TempListModel
@using (Html.BeginForm("temptest", "Test", new { id = 1 }, FormMethod.Post, new { id = "listForm", name = "listForm" }))
{
<ul>
@for (int i = 0; i < Model.Comment.Count(); i++)
{
<li>
<div class="llformlabel">
Notes:
<div>@Model.Comment[i]</div>
@Html.TextArea("Comment", Model.Comment[i], 4, 63, new { @id = "Comment_" + i, @title = "Comment" })</div>
</li>
}
</ul>
<input type="submit" value="Save Changes" />
}
Controller Code
using System.Collections.Generic;
using System.Web.Mvc;
using UI.Models.Forms;
namespace UI.Controllers
{
public class TestController : Controller
{
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TempTest(TempListModel model)
{
//This function executes after the user submits the form.
//If server side validation fails then the user should be shown the form as it was when they submitted.
//model.Comment = GetComments(); //In my real world example this comes from a database.
if (true) //!ModelState.IsValid) //In my real world code this is a validation step that may fail
{
return View(model);
}
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult TempTest(int? id)
{
//In the real world example there is a lot going on in this function.
//It is used to load data from databases and set up the model to be displayed.
var model = new TempListModel();
model.Comment = GetComments();
return View("TempTest", "TempLayout", model);
}
private static IList<string> GetComments()
{
//Simple sample function used for demo purposes.
IList<string> comments = new List<string>();
comments.Add("Comment 1");
comments.Add("Comment 2");
comments.Add("Comment 3");
return comments;
}
}
}
<textarea> does not support the value attribute.
Use the <textarea> tag to show a text area. The HTML <textarea> tag is used within a form to declare a textarea element - a control that allows the user to input text over multiple rows. Specifies that on page load the text area should automatically get focus.
The <textarea> element is often used in a form, to collect user inputs like comments or reviews. A text area can hold an unlimited number of characters, and the text renders in a fixed-width font (usually Courier). The size of a text area is specified by the cols and rows attributes (or with CSS).
We can [AllowHtml] attribute on properties in model or view model to disable request validation. [AllowHtml] attribute allows a request to include HTML markup during model binding by skipping request validation for the property.
If you fail validation just return the model.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TempTest(TempListModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("TempTest");
}
return View(model);
}
Edit Try this in your view instead
@for (int i = 0; i < Model.Comment.Count(); i++)
{
<li>
@Html.TextAreaFor(m => m.Comment[i], 4, 63, new { @title = "Comment" })
</li>
}
And let the helper name the elements for you. You end up with name
attributes like Comment[i]
.
ASP.NET MVC default ModelBinder looks for HTML names in the request that match TempListModel properties to build the model back in the server. But you are overriding the comment Id of each HTML Element:
@Html.TextArea("Comment", Model.Comment[i], 4, 63, new { @id = "Comment_" + i, @title = "Comment" })
If you need to place this custom ID, you must create a new ModelBinder. You can keep things easy like this:
@Html.TextAreaFor(m => m.Comment[i], 4, 63, new { @title = "Comment" })
Hopes Its help you!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With