Selecting multiple from #Html.ListBoxFor - asp.net-mvc

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; }

Related

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>
}

Html.BeginForm call the right Action in Controller

There are a lot of topics related to this question but I still did't figured out what I'm doing wrong.
I have a database where I manage access of different users to folders. On my View the User can select Employees which should have access to certain folder. Then I want to pass the selected Employees to Controller, where the database will be updated.
My Problem is: The right Action in the Controller class didn't get invoked.(I have a breakpoint inside)
Here is the View
#model DataAccessManager.Models.EmployeeSelectionViewModel
#{
ViewBag.Title = "GiveAccessTo";
}
#using (Html.BeginForm("SubmitSelected", "FolderAccessController", FormMethod.Post, new { encType = "multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.fr_folder_uid_fk)
<div class="form-horizontal">
<input type="submit" value="Save" id="submit" class="btn btn-default" />
<table id="tableP">
<thead>
<tr>
<th>Selection</th>
<th>Second Name</th>
<th>First Name</th>
<th>Department</th>
</tr>
</thead>
<tbody id="people">
#Html.EditorFor(model => model.People)
</tbody>
</table>
</div>
</div>
</div>
}
Here is the Controller reduced to the minimum
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SubmitSelected(EmployeeSelectionViewModel model)
{
return View();
}
More Details: I am not sure what is causing the problem, so here some more details.
The view is strongly typed to EmployeeSelectionViewModel, it represets the table with all Employees as a List here is the the code:
public class EmployeeSelectionViewModel
{
public List<SelectEmployeeEditorViewModel> People { get; set; }
public EmployeeSelectionViewModel()
{
this.People = new List<SelectEmployeeEditorViewModel>();
}
public Int64 fr_folder_uid_fk { get; set; }
public IEnumerable<string> getSelectedIds()
{
// Return an Enumerable containing the Id's of the selected people:
return (from p in this.People where p.Selected select p.fr_mavnr_fk).ToList();
}
}
The SelectEmployeeEditorViewModel represents one row of the table with all Employees.
public class SelectEmployeeEditorViewModel
{
public bool Selected { get; set; }
public string fr_mavnr_fk { get; set; }
public string firstName { get; set; }
public string secondName { get; set; }
public string dpt { get; set; }
}
And it has a View which create the checkboxes for each Employee
#model DataAccessManager.Models.SelectEmployeeEditorViewModel
<tr>
<td style="text-align:center">
#Html.CheckBoxFor(model => model.Selected)
#Html.HiddenFor(model => model.fr_mavnr_fk)
</td>
<td>
#Html.DisplayFor(model => model.secondName)
</td>
<td>
#Html.DisplayFor(model => model.firstName)
</td>
<td>
#Html.DisplayFor(model => model.dpt)
</td>
</tr>
The /FolderAccessController/SubmitSelected URL is called in the browser when I press the Submit button, but as mentioned the Action isn't invoked.
EDIT: I get the HTTP 404 not found error after pressing the button
Try removing the "Controller" word from your Html.BeginForm() second parameter, it's not needed.
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post, new { encType = "multipart/form-data"}))
Thiago Ferreira and haim770 Thanks a lot! The solution is to use the combination of your comments. So:
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post))
at the Controller

MVC model data that is rendered in the view is null when posted back

I have seen similar questions to this and followed the routine answer which is to ensure all model data is rendered in the HTML.
I have done that and the model is rendered in the view with #Html.HiddenFor() but when the posting back to the controller there are no items in the list ?
The view will happily render multiple items in the list, but List<Item> Items in the posted data is always an empty list (not null)
Model
public class ItemCollection
{
public List<string> AvailiableActions { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string SelectedAction { get; set; }
}
View
#model ItemCollection
#using (Html.BeginForm("myAction", "myController", FormMethod.Post))
{
<fieldset>
<div>
#Html.HiddenFor(m => Model.Items)
#Html.DisplayNameFor(x => x.AvailiableActions)
<table>
#{
foreach (var item in Model.Items)
{
#Html.HiddenFor(m => item)
#Html.HiddenFor(s => item.Id)
<tr>
<td>#item.Name</td>
<td>#Html.DropDownList(item.SelectedAction, new SelectList(Model.AvailiableActions))</td>
</tr>
}
}
</table>
</div>
</fieldset>
}
Controller
[HttpPost]
private ActionResult myAction(ItemCollection model)
{
if (model.Items.Count() == 0)
{
// this is true.. something is wrong......
}
}
You cannot use a foreach loop to render controls for a collection. It renders duplicate id and name attributes without the necessary indexers to bind to a collection. Use a for loop
for (int i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[i].Id)
#Html.DisplayFor(m => m.Items[i].Name)
</td>
<td>#Html.DropDownList(m => m.Items[i].SelectedAction, new SelectList(Model.AvailiableActions))</td>
</tr>
}
Note your view also included #Html.HiddenFor(m => Model.Items) and #Html.HiddenFor(m => item) which would also have failed because item is a complex object and you can only bind to value types. You need to remove both.
Instead of iterating over all items to make sure the index is added to the generated output, you may consider using EditorTemplates (an example on an other site).
EditorTemplates allow you to specify a template for a single Item in \Views\Shared\EditorTemplates\Item.cshtml:
#model Item
#{
var options= (List<string>)ViewData["Options"];
}
<tr>
<td>
#Html.HiddenFor(m => m.Id)
#Html.DisplayFor(m => m.Name)
</td>
<td>#Html.DropDownList(m => m.SelectedAction, new SelectList(options))</td>
</tr>
Then you may simply change your view to:
#model ItemCollection
#using (Html.BeginForm("myAction", "myController", FormMethod.Post))
{
<fieldset>
<div>
<table>
#Html.EditorFor(m => m.Items, new {Options = Model.AvailiableActions })
</table>
</div>
</fieldset>
}

Post Multiple Data from View to Controller MVC

I want to post quantity property to Controller (It's an edit action). I'm editing OrderedProductSet which is connected with ProductSet in my SQL Database (I get the name and price from there). How to pass multiple data from the view to controller? How to write method in controller class to receive the data (I'm asking about method arguments in this specific case).
My view:
#model Shop.Models.ProductViewModel#{
ViewBag.Title = "Edycja zamówienia";
}
<h2>Edycja zamówienie</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
<b>Nazwa produktu</b>
</th>
<th>
<b>Cena</b>
</th>
<th>
<b>Ilość</b>
</th>
<th></th>
</tr>
#foreach (var item in Model.orderedProductSet)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.price)
</td>
<td>
#Html.EditorFor(model => item.quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Potwierdź zmiany" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Powrót", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My model (in separated classes of course):
public class ProductViewModel
{
public OrderSet orderSet { get; set; }
public IEnumerable<OrderedProductSet> orderedProduktSet { get; set; }
}
public partial class OrderedProduktSet
{
public int orderNumber{ get; set; }
public int productNumber { get; set; }
public int ilosc { get; set; }
public virtual ProduktSet ProduktSet { get; set; }
public virtual OrderSet OrderSet { get; set; }
}
You need to construct controls for you collection in a for loop or use a custum EditorTemplate for OrderedProduktSet so that the controls are correctly named with indexers and can be bound on post back. Note the for loop approach required that the collection be IList.
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
for(int i = 0; i < Model.orderedProductSet.Count; i++)
{
#Html.DisplayFor(m => m.orderedProductSet[i].ProduktSet.name)
....
#Html.EditorFor(m => m.orderedProductSet[i].quantity, new { htmlAttributes = new { #class = "form-control" } })
}
<input type="submit" />
}
Controller (the model will be bound, including the collection of OrderedProductSet)
public ActionResult Edit(ProductViewModel model)
{
....
}
Alternatively, you can create an EditorTemplate
/Views/Shared/EditorTemplates/OrderedProduktSet.cshtml
#model OrderedProduktSet
#Html.DisplayFor(m => m.ProduktSet.name)
#Html.EditorFor(m => m.quantity, new { htmlAttributes = new { #class = "form-control" } })
and in the main view
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
#Html.EditorFor(m => m.orderedProductSet)
<input type="submit" />
}
Viewbag is your friend here. You normally pass data from View to Controller in MVC. You can access data set in a Viewbag in the controller in your View.
The simplest way to let your controller handle your view is to create an actionresult method in your controller with the same name as your view.
For example, your view is called Index, thus you would have the following method in your controller to handle the view data:
public ActionResult Index()
{
return View();
}
Accessing a list:
Use a Viewbag.
Controller
Viewbag.MyList = myList
View
#foreach (var item in Viewbag.MyList)
Here is good link for more info:
http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-a-view

Get Value of Property (List<long>) in Post Action in ASP.NET MVC3

This is My model:
public class MyModel
{
public List<long> NeededIds { get; set; }
public string Name { get; set; }
}
My Controllers:
public ActionResult Create()
{
MyModel model = new MyModel();
model.NeededIds = new List<long> { 1, 2, 3, 4 };
return View(model);
}
[HttpPost]
public ActionResult Create(MyModel model)
{
string name = model.Name;
List<long> ids = model.NeededIds;
return RedirectToAction("Index");
}
And View:
#model TestMVC.Models.MyModel
#using(Html.BeginForm()) {
<table>
<thead>
<tr>
<th>
Id
</th>
</tr>
</thead>
<tbody>
#foreach(long id in Model.NeededIds) {
<tr>
<td>
#id
</td>
</tr>
}
</tbody>
</table>
#Html.ValidationSummary(true)
<fieldset>
<legend>MyModel</legend>
<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>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
I set NeededIds in Get action and in the view I can see NeededIds. I also need it in Post action, but in post action the NeededIds is always null. How can I get the property value in post action when I set it in get action? What is your suggestion?
You are not posting your NeededIds back to the server. In order to get this working you can add them as hidden fields in a for loop inside the form:
#for (int i = 0; i < Model.NeededIds.Count(); i++) {
#Html.HiddenFor(model => model.NeededIds[i])
}
if you are using layout page than simply remove the form tag from the layout page.
in addition to the answer by Yakimych
you have kept the ids as constant.. this means two things
1. you can use arrays in place of list
2.you can just save the ids list/array in TempData and retrive it back from there when POST happens
you can do this like this
in your GET handler
TempData.Add("ids",idArray);
in your POST handler
var idArray = (long[])TempData["ids"];

Resources