ASP.NET MVC FormCollection TextArea - asp.net-mvc

I have a textarea that represents a description field. The descriptions have commas so when trying to split the field's descriptions the data is not parsed correctly. How can I get each row's description correctly.
var DescList = FormValues["Item.Description"].Split(',').Select(item => item).ToList<string>();
//will not work for obvious reasons. Comma delimited FormCollection has commas to identify separate row data.
It seems like Microsoft designed the FormsCollection without the textarea control in mind. A text area with commas will not work when trying to access each value. What is interesting is that the _entriestables property has it in the perfect format but they chose to make it a private property. Very frustrating.
`
Here is the important part of my viewmodel.
public class TenantViewModel
{
public Tenant Tenant { get; set; }
public Site Site { get; set; }
}
My view is populated like this:
if (Model != null && Model.Tenant != null && Model.Tenant.Site != null && Model.Tenant.Site.Count() > 0)
{<div class="detailsbox_view">
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(item => item.Site.Title)</th>
<th>#Html.LabelFor(item => item.Site.Description)</th>
</tr>
#foreach (var Item in Model.Tenant.Sites)
{
<tr>
#Html.HiddenFor(modelItem => Item.SiteId)
<td>
#Html.EditorFor(modelItem => Item.Title)
</td>
<td>
#Html.TextAreaFor(modelItem => Item.Description, new {#width="400" })
</td>
</tr> }
</table>
As you see this site table is a child of Tenant object. This child record does not get automatically updated using this method but the Tenant data does automatically get updated. This is the reason I tried the FormColelction instead.
Is there something I am missing to make this work?

try with this useful function
ValueProviderResult Match=FormCollection.GetValue("ValueProvider");

When you have multiple fields with the same name attribute, they'll come back into your FormCollection as an array. So upon posting a view like this:
<form action="/Home/MyAction">
<textarea id="row_one_description" name="description">
First row's description
</textarea>
<textarea id="row_two_description" name="description">
Second row's description
</textarea>
<input type="submit" value="Submit" />
</form>
you could do something like this in your action
[HttpPost]
public ActionResult MyAction(FormCollection collection)
{
var descriptionArray = collection["description"];
string firstRowDescription = descriptionArray[0];
string secondRowDescription = descriptionArray[1];
}
I must note that this is not the recommended way of dealing with posted data. You should instead be building your view using data from a view model and using strongly typed html helpers to render your controls. That way when you post, your action can take the ViewModel as a parameter. Its properties will be automatically bound and you will have a nice object to play with.
[HttpPost]
public ActionResult MyAction(MyViewModel viewModel)
{
foreach (var row in viewModel.Rows)
{
string description = row.Description;
}
}
EDIT
I'm still assuming a lot about your ViewModel but perhaps try this:
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(model => model.Site.Title)</th>
<th>#Html.LabelFor(model => model.Site.Description)</th>
</tr>
#for (var i = i < Model.Tenants.Sites.Count(); i++) {
<tr>
#Html.HiddenFor(model => model.Tenants.Sites[i].SiteId)
<td>
#Html.EditorFor(model => model.Tenants.Sites[i].Title)
</td>
<td>
#Html.TextAreaFor(model => model.Tenants.Sites[i].Description, new { #width="400" } )
</td>
</tr>
}
</table>

You could also try ,
string Match=FormCollection.GetValue("ValueProvider").AttemptedValue;

Related

How to post back a list of object to the controller in MVC [duplicate]

This question already has answers here:
MVC Form not able to post List of objects
(3 answers)
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 6 years ago.
I have an object model and I am passing it to the view so that user can input their comments for some of the object's properties as a part of a survey.
I am able to receive what the user has entered for an object if I am only rendered one single object to the view. However, when I want to render multiple (a list) of objects to the view then I receive a null list of objects when the user click on the submit form.
Please see my code below:
This is my object model
public class SurveyViewModel
{
public string Name { get; set; }
public double PV { get; set; }
public double QtyUsePerMonth { get; set; }
public double TotalPVPerMonth { get; set; }
}
This is my view where I render the list of object
#model IEnumerable<WebApplication4.Models.SurveyViewModel>
#{
ViewBag.Title = "Survey";
}
<h2>Survey</h2>
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "form1" }))
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PV)
</th>
<th>
#Html.DisplayNameFor(model => model.QtyUsePerMonth)
</th>
<th>
#Html.DisplayNameFor(model => model.TotalPVPerMonth)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
#Html.HiddenFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.PV)
#Html.HiddenFor(modelItem => item.PV)
</td>
<td>
#Html.TextBoxFor(modelItem => item.QtyUsePerMonth)
</td>
<td>
#Html.TextBoxFor(modelItem => item.TotalPVPerMonth)
</td>
</tr>
}
</table>
<input type="submit" value="Submit">
}
And this is my HttpPost method
[HttpPost]
public ActionResult Survey(List<Models.SurveyViewModel> model)
{
...
}
When the user click on the Submit button I got a null for model where I am expecting to see a list.
Please let me know what I am doing wrong.
Thank you for your help.
Normally, you can't post the list of objects to the controller in default model binder.
Best solution you can pass the object only based on Index to perform the CRUD operation in POST, and GET request you could get all list of objects. So in POST method it works if pass the object only.
If you want to post the list of object, you can achieve it by overriding the ModelBinder or using the FormCollection.
Here is an example, but to perform this way of operation we need to iterate and convert into the list. Because formcollection contains more number of items and not in the List type. The key of the property varies, because of html helper. Be aware when getting the value from formcollection.
[HttpPost]
public ActionResult Index(FormCollection model)
{
List<SurveyViewModel> obj = new List<SurveyViewModel>();
var name =Request.Form["item.Name"].Split(',').ToArray();
var pv =Request.Form["item.PV"].Split(',').ToArray();
//length must be same
for (var i = 0; i < name.Length; i++)
{
obj.Add(new SurveyViewModel()
{
Name = name[i],
PV = Convert.ToDouble(pv[i])
});
}
return View();
}

The edit button is deleting from the database instead of saving to it MVC

I have posted the relevant code to this issue below. My problem is, let's say, the database is displaying NA, I want to edit it and put in 1.1, or some number. Instead of updating and saving this new number, it deletes NA and does not update or save anything, so I know it is doing something, but I'm not sure where I have gone wrong. If I change the type in the model to int or object, it gives an error for conversion to string. Can someone help please? Thank you!
Controller:
public ActionResult Edit ()
{
return View ();
}
[HttpPost]
public ActionResult Edit(MyIssue issues)
{
var model = new Test.Models.Tables();
if (ModelState.IsValid)
{
db.Entry(issues).State = EntityState.Modified;
issues.Number = model.Number;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(issues);
}
Model:
namespace Test.Models
{
public class Tables: DbContext
{
public string Number { get; set; }
}
}
View:
<td>
#if (issue.Number != null)
{
#Html.HiddenFor(modelItem => issue.Number)
#Html.DisplayFor(modelItem => issue.Number)
<text>|</text>
<h5 id="editclass">
#Html.ActionLink("Edit", "Page1", new { id = issue.Number })
</h5>
using (Html.BeginForm())
{
#Html.ValidationSummary(true) {
<fieldset>
#Html.HiddenFor(modelItem => issue.Number)
<div class="editor-field">
#Html.EditorFor(modelItem => issue.Number)
#Html.ValidationMessageFor(modelItem => issue.Number)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
}
}
</td>
From your code, I assume that the code you shown is inside a loop where issue is the loop iterator variable. So razor will generate an input field with name "issue.Number". When the form is submitted, model binder cannot bind this form value to the Number property of your MyIssue object ! So it gets the default null value and your code is assigning the null value as the Number property and saving it.
You should generate an input field with name="Number". You may use the Html.TextBox helper method to do so.
#foreach (var issue in SomeCollection)
{
<tr>
<td>
#using (Html.BeginForm())
{
<!-- Other fields also goes here -->
#Html.TextBox("Number",issue.Number)
<input type="submit" />
}
</td>
</tr>
}

mvc razor view nested foreach model post null subobject

In my code I cannot read a nested object value on form post.
Wrog way to edit List property in one Object:
#{
var contatore = 0;
foreach (var item in Model.Movimenti)
{
var movimento = item;
<tr>
<td align="left">
#*Imposto gli Hidden per tutte le proprietà che devo recuperare al post*#
#Html.HiddenFor(x => movimento.Prodotto.Descrizione, "Movimenti[" + contatore + "].Prodotto.Descrizione")
#Html.DisplayFor(x => movimento.Prodotto.Descrizione, "Movimenti[" + contatore + "].Prodotto.Descrizione")
</td>
<td>#Html.EditorFor(x => movimento.Aum, "CurrencyDisabled", "Movimenti[" + contatore + "].AUM")</td>
</tr>
contatore++;
}
}
This is the correct way to edit List property in one Object:
The code:
#using AI.Business.Models
#model Operazione
#{ ViewBag.Title = "Simulatore"; }
#using (Html.BeginForm("CreaOperazione", "Operativita", FormMethod.Post))
{
// Imposto gli Hidden per tutte le proprietà che devo recuperare al post
#Html.HiddenFor(x => x.RapportoModel.TipoRapportoId)
<table width="100%" class="display" id="Simulatore" cellspacing="0">
<thead>
<tr>
<th class="dt-head-left">Linea</th>
<th>AUM</th>
</tr>
</thead>
<tbody>
#Html.EditorFor(x => x.Movimenti)
</tbody>
</table>
<button id="btnSalva" name="btnSalva" type="submit" style="float: right;">Salva Operazione</button>
}
With the editor assuggested:
#model AI.Business.Models.Movimento
<tr>
<td align="left">
#Html.HiddenFor(x => x.Prodotto.Descrizione)
#Html.DisplayFor(x => x.Prodotto.Descrizione)</td>
<td>#Html.EditorFor(x => x.Aum, "CurrencyDisabled")</td>
And this is my object:
public class Movimento
{
public int Id { get; set; }
public ProdottoModel Prodotto { get; set; }
public decimal Aum { get; set; }
}
And the Object Prodotto:
public class ProdottoModel
{
[Key]
public int ID { get; set; }
public string Descrizione { get; set; }
}
In my Actionresult the property Descrizione is null:
[HttpPost]
public ActionResult CreaOperazione(Operazione operazione)
{
if (ModelState.IsValid)
{
// Do something
}
else
ImpostaErrore(ModelState);
return View("PaginaSimulatore", operazione);
}
Open the images:
At my first access to the page the property Prodotto.Descrizione is populated
When i raise the form post event this property was sent with a null value
I'm not sure how you're getting any of this to work, but it's a total fluke. HiddenFor, for example, has no parameter that let's you specify the name value for the field. Instead, where you're trying to do that, the parameter is actually for htmlAttributes, which expects either an anonymous object or IDictionary. The only reason you aren't getting errors is because string is technically an object, but it will never do anything in this context.
The same goes for the rest of your helper calls. With EditorFor, in particular, the second param where you're passing "CurrencyDisabled", is for specifying the editor template that should be used, and the third param is for additionalViewData, which just appends items to ViewData within the context of the editor template.
Long and short, none of this works how you think it does. Plainly and simply, if you need to work with a collection, you need to use for rather than foreach. The expression that you pass to the *For family of helpers is not just about identifying a property however you can get to it; it must be a bindable expression, i.e. something Razor can use to create a name for the form field that will line up to something on your model on post. In order for that to happen, the names must be something like Movimenti[N].Prodotto.Descrizione, and the only way to get that is to call the helper like:
#Html.HiddenFor(m => m.Movimenti[i].Prodotto.Descrizione)
Where i would be the iterator from your for loop.

Selecting multiple from #Html.ListBoxFor

I have added #Html.ListBoxFor i am able to bind it properly but unable to get multiple selected items after submit. Am i missing something ?
// Below is the code for binding
public ActionResult Create()
{
var cities = So.BL.City.GetCities();
SelectList cityList = new SelectList(cities, "Id", "Name", cityId);
TempData["Cities"] = cityList;
return View("Create");
}
[HttpPost]
public ActionResult Create([Bind(Include="Keywords,Cities")] So.Entities.Approval filter)
{
if (ModelState.IsValid)
{
}
return View(filter);
}
Below is the view file code. I dont have a view model just entities
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table style="width: 100%">
<tr>
<td>
Cities:
</td>
</tr>
<tr>
#* <td>
#Html.DropDownList("Cities", (SelectList)TempData["Cities"])
</td>*#
</tr>
<tr>
<td>
#Html.ListBoxFor(model => model.Id, new SelectList(TempData["Cities"] as MultiSelectList, "Value","Text",new { style = "width:250px" })))
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.Keywords)
</td>
</tr>
<tr>
<td>
#Html.EditorFor(model => model.Keywords)
</td>
</tr>
<tr>
<td>
</td>
</tr>
<tr>
<td>
<input type="submit" value="Create" class="button" />
</td>
</tr>
</table>
Assuming you have changed the ID property to typeof int[] as per the comments, you still have problems, the main one being that your POST method has
[Bind(Include="Keywords,Cities")]
which excludes binding of the ID property so it will always be null on post back. When ever you use the [Bind] attribute you should reconsider what you doing and use a view model to display/edit just the properties you want, including a property for the SelectList.
You also have some pointless code in the #Html.ListBoxFor() method. TempData["Cities"] is already a SelectList so new SelectList(TempData["Cities"] as MultiSelectList, "Value","Text" is converting the SelectList to a MultiSelectList and then creating a new SelectList form it. All you need is
#Html.ListBoxFor(model => model.ID, (SelectList)TempData["Cities"], new { style = "width:250px" })
In addition, the 3rd parameter in this
SelectList cityList = new SelectList(cities, "Id", "Name", cityId);
is not required (not sure where you declared cityId because as it is, your code does not compile). The ListBoxFor() method selects the options based on the value of your ID property, and ignores the selectedValue parameter in the SelectList constructor.
Finally, in your POST method, if the model is not valid you return the view. You need to reassign the value of TempData["Cities"] or this will be null in the view and throw an exception.
Like Andrei is suggesting, you need to bind the selected value to an array type.
Front End:
<div class="DualListBoxDIV">
#Html.ListBoxFor(model => model.RelatedNewsArticlesSelected, new MultiSelectList(Model.AssignedRelatedNewsArticles, "Value", "Text", Model.RelatedNewsArticlesSelected), new { #class = "SelectedBox", Size = 10 })
</div>
Back End:
public string[] RelatedNewsArticlesSelected { get; set; }
public IEnumerable<SelectListItem> AssignedRelatedNewsArticles { get; set; }
public IEnumerable<SelectListItem> UnassignedRelatedNewsArticles { get; set; }

How do I return List or Collection to Controller from View in MVC 3?

Someone please help me return this list properly from my view. I don't see why I'm returning null for my fieldModelList I try to pass to the controller...
Here is my view:
#model List<Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel>
<script type="text/javascript" src="~/Scripts/jquery-ui-1.8.11.min.js"></script>
#using (Html.BeginForm("GetResponse", "TestMethods", FormMethod.Post))
{
<table id="tblMethods">
<tr>
<th>
Property Name
</th>
<th>
Request
</th>
</tr>
#foreach (FieldModel fieldModel in Model)
{
<tr>
<td>
#Html.DisplayFor(m => fieldModel.PropertyName)
</td>
<td>
#Html.TextBoxFor(m => fieldModel.PropertyValue)
</td>
</tr>
}
</table>
<div>
<input type="submit"/>
</div>
and here is my controller:
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I am hitting the HttpPost method but if I place a breakpoint just inside it, I am returning null for the fieldModelList right off the bat, which I was hoping would be a list of the values I entered into the texboxes on the view that is of model FieldModel...
I think something is wrong with my logic versus my syntax, or as maybe as well as my syntax, but basically what I want to do is return back a list of type FieldModel with each corresponding PropertyName and PropertyValue to the controller. I noticed I am not passing any kind of id parameter in my BeginForm statement in the view. Do I need one here?
Just in case, here is my model class for FieldModel:
namespace Regions.SOA.UI.CopyBookSchemaCreator.Models
{
public class FieldModel
{
[Display(Name = "Property")]
public string PropertyName { get; set; }
[Display(Name = "Value")]
public string PropertyValue { get; set; }
}
}
Phil Haack wrote an article some time ago explaining how to bind collections (ICollection) to view models. It goes into additional detail about creating an editor template, which you could certainly do as well.
Basically, you need to prefix the HTML elements' name attributes with an index.
<input type="text" name="[0].PropertyName" value="Curious George" />
<input type="text" name="[0].PropertyValue" value="H.A. Rey" />
<input type="text" name="[1].PropertyName" value="Ender's Game" />
<input type="text" name="[1].PropertyValue" value="Orson Scott Card" />
Then, your controller could bind the collection of FieldModel
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I'm not 100% sure the following would name the attributes correctly (I'd recommend using the editor template) but you could easily use the htmlAttributes argument and give it a name using the index.
#for(int i = 0;i < Model.Count;i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].PropertyName)
</td>
<td>
#Html.TextBoxFor(m => m[i].PropertyValue)
</td>
</tr>
}
Editor Template
If you wanted to go as far as adding an editor template, add a partial view named FieldModel.ascx to /Views/Shared that is strongly typed to a FieldModel
#model Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel
#Html.TextBoxFor(m => m.PropertyName) #* This might be a label? *#
#Html.TextBoxFor(m => m.PropertyValue)
And, then the part of your view responsible for rendering the collection would look like:
#for (int i = 0; i < Model.Count; i++) {
#Html.EditorFor(m => m[i]);
}

Resources