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

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"];

Related

Why list of checkbox selections always posted as null in ASP.NET MVC-5 [duplicate]

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 5 years ago.
I am new to ASP .NET MVC. My problem is - I want to 'POST' a collection of the items , so that controller can process it.
My model is collection of -
public class CheckedRentalProperty
{
public bool IsSelected { get; set; }
public int Id { get; set; }
public String Address { get; set; }
}
My controller is defined like this -
public class RentalPropertiesController : Controller
{
public ActionResult Index()
{
List<CheckedRentalProperty> checkHsList = new List<CheckedRentalProperty>();
// Fill the list
return View(checkHsList);
}
[HttpPost]
public ActionResult Save(IEnumerable<CheckedRentalProperty> checkHsList)
{
// why checkHsList is coming as null ??
}
}
And the view is like this -
#model IEnumerable<XXX.Models.CheckedRentalProperty>
#using (Html.BeginForm("Save", "RentalProperties", FormMethod.Post))
{
<div class="form-horizontal">
<div class="form-group">
<table class="table">
<tr>
<th>
</th>
<th>
#Html.DisplayNameFor(model => model.Address)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.CheckBoxFor(modelItem => item.IsSelected)</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
</tr>
}
</table>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
My expectations was - when I hit the "Save" button, the Model, which is IEnumerable<CheckedRentalProperty> item, will be passed to the Save() action of the controller. However, I find that the passed parameter is "null" all the time. What am I missing?
Model that are solely IEnumerable are not too friendly as MVC Model.
There are many issues arise here, but in a nutshell, MVC webform bindings needs form name requests to be send in the following format: PropertyName[Index].Property
Which is not the case at your example.
It is a good design practice, to create a wrapping ViewModel which will hold the properties you need for the given controller + pages.
ViewModel
public class RentalPropertiesViewModel
{
public List<CheckedRentalProperty> CheckedRentalProperties { get; set; }
}
Controller: Next we will want to use this ViewModel in our controller.
public ActionResult Index()
{
var checkHsList = new List<CheckedRentalProperty>();
checkHsList.Add(new CheckedRentalProperty { Id = 1, Address = "Address1", IsSelected = true });
checkHsList.Add(new CheckedRentalProperty { Id = 2, Address = "Address2", IsSelected = false });
checkHsList.Add(new CheckedRentalProperty { Id = 3, Address = "Address3", IsSelected = true });
var model = new RentalPropertiesViewModel
{
CheckedRentalProperties = checkHsList
};
return View(model);
}
[HttpPost]
public ActionResult Save(RentalPropertiesViewModel model)
{
// why checkHsList is coming as null ??
return null;
}
View: Now in our view we should set the Model as the new ViewModel type we created.
#model TestBindings.Models.RentalPropertiesViewModel
And our view form should be something like:
<table class="table">
<tr>
<th>
Is Selected
</th>
<th>
Address
</th>
</tr>
#for (int i = 0; i < Model.CheckedRentalProperties.Count(); i++)
{
<tr>
#Html.HiddenFor(model => model.CheckedRentalProperties[i].Id);
<td>#Html.CheckBoxFor(model => model.CheckedRentalProperties[i].IsSelected)</td>
<td>#Html.TextBoxFor(model => model.CheckedRentalProperties[i].Address)</td>
</tr>
}
</table>
I've use the following format model => model.CheckedRentalProperties[i].IsSelected and now MVC InputExtensions will bind it correctly. e.g: CheckedRentalProperties[0].IsSelected
Important Note: Notice i'm passing Id property as hidden, so MVC Binder will know to set the Id to the correct item.

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

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

how can i get the text box for a view? in my controller

I follow this example to make n to n relations
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
it work fine but for the n to n relations with payload to the database i figure out and I can do the [HttpGet] and show the view what i want to show but now i want to know how can i get the textbox I have in my view, I can get the checkbox in my controller (see see below the action) and this is my view so my question will be how can get the textbox too? in my controller for every checkbox?
#using (Html.BeginForm("AgregarEmpresas", "Empleado"))
{
<fieldset>
<div class="editor-field">
<table>
<tr>
#{
int cnt = 0;
List<ITCOrganigrama.ViewModel.AsignarEmpresa> empresas = ViewBag.Empresas;
foreach (var empresa in empresas)
{
if (cnt++ % 5 == 0) {
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
name="selectedEmpresa"
value="#empresa.EmpresaId"
#(Html.Raw(empresa.Assigned ? "checked=\"checked\"" : "")) />
#empresa.Nombre
<div>
#Html.LabelFor(model => empresa.cargo)
#Html.TextBoxFor(model => empresa.cargo, new { style = "width: 150px;" })
#Html.ValidationMessageFor(model => empresa.cargo)
</div>
#:</td>
}
#: </tr>
}
</table>
</div>
<p>
<input type="submit" value="Agregar" />
</p>
</fieldset>
}
the action where i get the chekbox
[HttpPost]
public ActionResult AgregarEmpresas(int? id, FormCollection formCollection, string[] selectedEmpresa)
{
}
my final view:
http://s3.subirimagenes.com:81/otros/previo/thump_7406511add1.jpg
http://www.subirimagenes.com/otros-add1-7406511.html
Edited:
ViewModel Class
public class AsignarEmpresa
{
public int EmpresaId { get; set; }
public string Nombre { get; set; }
public string cargo { get; set; }
public bool Assigned { get; set; }
}
Look at your post action and it's parameters. Names of those are really important.
Your checkboxes
<input type="checkbox"
name="selectedEmpresa"
value="#empresa.EmpresaId"
are working fine beacuse look at the name of the input its "selectedEmpresa" it's the same name as in Controller Action definition. Model binder looks for this name in posted data and if he finds it, he creates object from it. In your case he will try to parse data into string[] object.
So ... first of all you have to change action definition to something like.
[HttpPost]
public ActionResult AgregarEmpresas(int? id, FormCollection formCollection, string[] selectedEmpresa,string [] empresaTextBox)
{
}
Then you have to change generated html.
#Html.TextBoxFor(model => empresa.cargo, new { style = "width: 150px;", name="empresaTextBox" })
With those changes you should get some data inside Action. However you will get something weird beacuse you have multiple checkboxes and textboxes in order to tell model binder that there are multiple elements of those you have to prepare special name of inputs that contains index number.
Look at this example
<input name="childs[0]"></input>
<input name="childs[1]"></input>
In this case Model Binder will create array of objects that contains 2 of them.
So finally your code would have to look like this.
#using (Html.BeginForm("AgregarEmpresas", "Empleado"))
{
<fieldset>
<div class="editor-field">
<table>
<tr>
#{
int cnt = 0;
int i=0;
List<ITCOrganigrama.ViewModel.AsignarEmpresa> empresas = ViewBag.Empresas;
foreach (var empresa in empresas)
{
if (cnt++ % 5 == 0) {
#: </tr> <tr>
}
#: <td>
<input type="checkbox"
name="selectedEmpresa[#i]"
value="#empresa.EmpresaId"
#(Html.Raw(empresa.Assigned ? "checked=\"checked\"" : "")) />
#empresa.Nombre
<div>
#Html.LabelFor(model => empresa.cargo)
#Html.TextBoxFor(model => empresa.cargo, new { style = "width: 150px;" ,name=String.Format("empresaTextBox\[{0}\]",i) })
#Html.ValidationMessageFor(model => empresa.cargo)
</div>
#:</td>
i++;
}
#: </tr>
}
</table>
</div>
<p>
<input type="submit" value="Agregar" />
</p>
</fieldset>
}
If you will get this to work. Then i would try to create a class with one boolean and one string value. With this change you would operate on the array of classes instead of two arrays with strings.

Resources