Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML.Textarea Values in MVC Razor View

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:
First load of form without changes

Pre-Submit Form Changes:
Form with changes to the first input before submitting

Form as seen after first submit:
Form as seen after the first submission

Form as seen after second submit:
enter image description here

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;
        }
    }
}
like image 557
RacerNerd Avatar asked May 15 '13 21:05

RacerNerd


People also ask

Can we use value attribute in textarea?

<textarea> does not support the value attribute.

How do I view a textarea in HTML?

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.

Can I use textarea as input HTML?

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).

How do I allow HTML tags in ASP.NET MVC?

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.


2 Answers

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].

like image 109
Jasen Avatar answered Oct 04 '22 19:10

Jasen


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!

like image 40
Fals Avatar answered Oct 04 '22 20:10

Fals