In MVC I have a form that uses a lot of duplicate information. Basically they click a button and there is another "form"(text box drop down list etc) that pops up. When they click the submit button however all of that comes back with the same name. How would I go about either making the names different in the Post or be able to throw the items into a list?
My code:
#Html.TextBox("Textbox1", "", new { placeholder = "", size = "77", maxlength = "76" })
#Html.DropDownList("Country", ViewData["CountryOptions"] as SelectList,"Select", new { id = "Country"})</div>
<div class="pad5">
<span class="FieldLabel">Application *</span><span>#Html.DropDownListFor(model => model.ProductsID, Model.HowProductsAreUsedOptions, new { #class = "General", id = "General-1" })
#Html.DropDownListFor(model => model.ApplicationValue, Model.Options, "--Choose One--", new { id = "Option1", style = "display:none" })
</div>
These can be repeated up to 9 times by the time they submit. However this will give me this in the Post
FormID=8&Textbox1=This&Country=USA&ProductsID=1&ApplicationValue=2&
Textbox13=Is&Country=Canada&ProductsID=2&ApplicationValue=3&
Textbox14=A&Country=Canada&ProductsID=2&ApplicationValue=1&
Textbox15=Test&Country=Canada&ProductsID=1&ApplicationValue=8&
Textbox16=For&Country=Canada&ProductsID=2&ApplicationValue=1&
Textbox17=Stack&Country=USA&ProductsID=1&ApplicationValue=9&
Textbox18=Overflow&Country=USA&ProductsID=2&ApplicationValue=2
How can I make something so that way it will be able to seperate these into 7 different value sets instead of only giving me one of each?
If I were you I would create a strongly typed view model to represent your data.
One to represent each item with the properties within them.
public class FooModel
{
public string Text { get; set; }
public string Country { get;set;}
public int ProductsID { get; set; }
public int ApplicationValue { get; set; }
}
Then create a model to hold them and represent them
public class FooViewModel
{
public List<FooModel> Foos { get; set; }
}
You can then return an instance of FooViewModel from your controller.
Within your view you use the name indexing of the collection as follows:
#for (var i = 0; i < Model.Foos.Count; i++)
{
...
#Html.TextBoxFor(model => Model[i].Text)
#Html.DropDownListFor(model => Model[i]Country, ViewData["CountryOptions"] as SelectList,"Select")
#Html.HiddenFor(model => Model[i].ProductsID)
...
}
The HiddenFor's will post those values back too.
Now in your action you just need to change your parameter to take an instance of FooViewModel and they will all be populated server side.
For more info on it see here:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
Related
Hi i have three Fields in my view.That three fields are drop down. I want to pass the value to these fields when edit button is clicked. That is the values need to pass to that drop down fields. My view is mentioned below
In my view i have many drop downs but once i know how to pass the value to one drop down means i will do for another drop downs.
For Edit i create one view in sql and connect that view as EDMX file in my application.In this view(table) i have all fields which is in Visitors Form. That view name is View_VisitorsForm.
My Model(VisitorsViewModel)
public Nullable<System.DateTime> Date { get; set; }
public System.Guid VisitingID { get; set; }
public Nullable<System.Guid> EmployeeID { get; set; }
public string EmployeeName { get; set; }
public string Description { get; set; }
My Edit Code
public ActionResult Edit(Guid ?id)
{
WafeERPNEWEntities db = new WafeERPNEWEntities();
SelectList typelist = new SelectList(db.Employees.ToList(), "EmployeeID", "DisplayName", db.Employees);
ViewData["EmployeeName"] = typelist;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
VisitorsViewModel ObjVisitorsviewModel = new VisitorsViewModel();
View_VisitorsForm visit = db.View_VisitorsForm.Find(id);
visit.VisitingID = ObjVisitorsviewModel.VisitingID;
visit.VisitingDate = ObjVisitorsviewModel.Date;
visit.Description = ObjVisitorsviewModel.Description;
if (ObjVisitorsviewModel == null)
{
return HttpNotFound();
}
return View(ObjVisitorsviewModel);
}
My View Code
#Html.LabelFor(model => model.Date)
#Html.EditorFor(model => model.Date)
#Html.ValidationMessageFor(model => model.Date)
#Html.LabelFor(model => model.VisitingID)
#Html.EditorFor(model => model.VisitingID)
#Html.ValidationMessageFor(model => model.VisitingID)
#Html.LabelFor(model => model.EmployeeName)
#Html.DropDownList("EmployeeID", (IEnumerable<SelectListItem>)ViewData["EmployeeName"])
#Html.ValidationMessageFor(model => model.EmployeeName)
Now when i click the edit button it pass the value to this line
View_VisitorsForm visit = db.View_VisitorsForm.Find(id);
and also it getting visit.visitingID. But it is not getting the value in viewmodel .so the value will be empty in view.All values are empty VisitingID, Description, Date Fields are empty and in Employee drop down it won't show the value which i passed to this field it shows the first value in dropdown. so please any one tell me how to solve this issue. Actually I try to explain my issue as per my level best and if you didn't understand my issue or any one need my full code or need more code tell me . i ready to update my code again. but i need solution.
Advance Thanks..
use:
#Html.DropDownListFor(model => model.EmployeeID,(IEnumerable<SelectListItem>)ViewData["EmployeeName"])
The important part being "DropDownListFor". You are using "DropDownList".
Use the DropDownListFor helper method.
#Html.DropDownListFor(model => model.EmployeeID,
(IEnumerable<SelectListItem>)ViewData["EmployeeName"])
Now in your GET action, you need to set the EmployeeID property value of your view model.
public ActionResult Edit(int id)
{
var objVisitorsviewModel = new VisitorsViewModel();
// I am hard coding to 25.
// You may replace it with a valid Employee Id from your db table for the record
ObjVisitorsviewModel.EmployeeID= 25;
return View(objVisitorsviewModel);
}
A more clean solution is to not use ViewData to transfer the data you need to render the dropdown option. You can make your code more strongly typed by simply adding a new property to your view model
public class VisitorsViewModel
{
public List<SelectListItem> Employees { set;get;}
public Guid? EmployeeID { get; set; }
// Your existing properties goes here
}
Now in your GET action(create/edit), Instead of storing the data in ViewData, we will load to the Empenter code hereloyees property.
public ActionResult Edit(int id)
{
var vm = new VisitorsViewModel();
vm.Employees = db.Employees.Select(s=> new SelectListItem {
Value=s.EmployeId.ToString(), Text=s.DisplayName }).ToList();
return View(vm);
}
And in your view, we will use the DropDownListFor helper method with the Employees property
#model VisitorsViewModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(s=>s.EmployeeID,Model.Employees,"Select")
}
You are using a DropDownList(...) instead of a DropDownListFor(...)
Your Model
You must add a SelectList:
public SelectList Employees { get; set }
Your Edit
You must get your employees list and add it to your model:
// Get employees list from the database
var employees = db.Employee.Select(x => x.Id, x.Name).Tolist();
// Put the employees in a SelectList
var selectList = new SelectList(employees .Select(x => new { value = x.Id, text = x.Name }), "value", "text");}).ToList();
// Pass the list to your ViewModel
ObjVisitorsviewModel.Employees = selectList;
Your View
Finally, change your DropDownListFor line for this:
#Html.DropDownListFor(model => model.EmployeeID,
model.Employees)
By using DropDownList(...), your object data is not bound to the DropDown. You must manage its selected value manually.
Html.CheckBox("SelectedStudents", false, new { #class = "check-item", id = x.Id, value = x.Id })
which produce
<input checked="checked" class="check-item" id="4507" name="SelectedStudents" value="4507" type="checkbox">
<input checked="checked" class="check-item" id="4507" name="SelectedStudents" value="4508" type="checkbox">
<input checked="checked" class="check-item" id="4507" name="SelectedStudents" value="4509" type="checkbox">
In mvc model I have
public IEnumerable<string> SelectedStudents { get; set; }
but when I post back, SelectedStudents are always null. Why?
In this howto http://benfoster.io/blog/checkbox-lists-in-aspnet-mvc
is written:
The ASP.NET MVC modelbinder is smart enough to map the selected items
to this property.
but in my example is always null. Why? How to write more checkboxes and bind it back
You should be using a strongly typed editor to be able to pass the result to the controller (Model binder).
I prefer to do it this way.
Model
public class YourViewModel
{
public List<SelectListItem> Students
{
get;
set;
}
}
Controller Get
Students= service.GetStudents(); //Fill the list
View
#for (var i = 0; i < Model.Students.Count; i++)
{
#Html.CheckBoxFor(m => m.Students[i].Selected)
#Html.HiddenFor(m => m.Students[i].Text)
#Html.HiddenFor(m => m.Students[i].Value)
<span>#Model.Students[i].Text</span>
}
Controller Post
[HttpPost]
public ActionResult Create(YourViewModel model)
{
foreach(var student in model.Students)
{
if(student.Selected) { // Do your logic}
}
}
Alternatively
You could use an array or List of string. A ListBox is used in this example.
public string[] SelectedStudents{ get; set; }
#Html.ListBoxFor(s => s.SelectedStudents, new MultiSelectList(Model.Students, "Value", "Text", Model.SelectedStudents), new { #class = "form-control", style = "height:250px; width:100%" })
See my answer here How to bind checkbox values to a list of ints?.
The nice thing about this is that it separates concerns between your controller and ui nicely. The html extension methods also create correct html using label and input for the checkbox. and there is no need for hidden fields.
Do you try it with CheckBoxListFor?? You need to associate the checkbox with model and should not have the same ID and name
#Html.CheckBoxListFor(model => model.SelectedSources, Model.SubscriptionSources)
You need to use a mutable type, like List<string>.
public List<string> SelectedStudents { get; set; }
I'm just picking up .net MVC and I've come across something that I can't work out. I'm obviously missing some basic principle but would love some help.
I have a ViewModel with two IEnumerables that I want to use to create dropdownlistfors. My GET works fine, the lists are populated as expected.
Now I'm posting the ViewModel back to a POST method, not to do anything useful but just to try and understand how mvc works. I expected that I would simply be able to re-populate the dropdownlistfors from the model that was posted back - but I get a null reference exception.
Other values, such as partyid, in the ViewModel survive the POST so i'm confused.
I can get it to work if I repopulate the lists but that seems wrong.
Can someone give me a pointer?
My ViewModel
public class DemoViewModel
{
//properties
public IEnumerable<tbl_server_lookup> servers { get; set; }
public int serverId { get; set; }
public IEnumerable<tbl_site_lookup> sites { get; set; }
public int siteId { get; set; }
public int partyid { get; set; }
public string message { get; set; }
public DemoViewModel()
{
}
}
My Controller
// GET: /Demos/Test/
[HttpGet]
public ActionResult Test()
{
DemoViewModel demo = new DemoViewModel();
using (var dbContext = new ADAPI.Models.db_ad_apiEntities2())
{
var serverList = dbContext.tbl_server_lookup.Where(s => s.server_name != null);
demo.servers = serverList.ToList();
var siteList = dbContext.tbl_site_lookup.Where(w => w.site_name != null);
demo.sites = siteList.ToList();
}
demo.message = "Enter the user id you would like to look up in the box below.";
return View(demo);
}
//
//POST: /Demos/Test/
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Test(DemoViewModel demo)
{
//It works if I uncomment this block...
/*using (var dbContext = new ADAPI.Models.db_ad_apiEntities2())
{
var myQuery = dbContext.tbl_server_lookup.Where(s => s.server_name != null);
demo.servers = myQuery.ToList();
var siteList = dbContext.tbl_site_lookup.Where(w => w.site_name != null);
demo.sites = siteList.ToList();
}*/
demo.message = "the user id you posted is: " + demo.partyid + ". The Server you selected is: ";// +demo.serverId;
return View(demo);
}
My View
#model ADAPI.ViewModels.DemoViewModel
<h2>Demos</h2>
<h3>#Model.message</h3>
#using (Html.BeginForm("Test","Demos"))
{
#Html.AntiForgeryToken()
<div class="">
<h4>Party ID</h4>
#Html.ValidationSummary(true)
<!-- input box for party id-->
#Html.TextBoxFor(model => model.partyid)
<!-- dropdown list of server types eg live vs test-->
#Html.DropDownListFor(model => model.serverId, new SelectList(Model.servers, "server_Id","server_name"))
#Html.DropDownListFor(model => model.siteId, new SelectList(Model.sites, "site_short_name","site_name"))
<input type="submit" value="Try" />
</div>
}
The Error
In MVC, model on the views are loaded in the controller action, they are not posted back along with the post action.
If you are used to ASPX's viewstate, there is no such thing in MVC, you need to load what you need for every view in the current action.
Dropdown lists are rendered into html as tag and returned to the server as plain single value.
You have to rebind/repopulate them on the server, wchich is annoying in scenarios like validation, where the same model should be returned to the client.
There is no support for that in the framework - you have to do it on your own.
One more thing - if you absolutely have to return the list items and want them back on the server, you can serialize tham and hide in some hidden field. But it's ugly and unsecure since anyone can change its value.
I'm trying to do a filtering page for a report using Asp.Net MVC4 with Razor. To achieve this, I developed a ViewModel-like class containing three secondary viewmodel object lists (which I will present them on small grids inside the page)
public class DeviationReportViewModel
{
[Display(Name = "Type")]
public int Type { get; set; }
[Display(Name = "Companies")]
public List<Company> Companies { get; set; }
[Display(Name = "Departments")]
public List<Department> Departments { get; set; }
[Display(Name = "Employees")]
public List<Employee> Employees { get; set; }
public List<Event> Events { get; set; }
[Display(Name = "Event")]
public Event ChosenEvent { get; set; }
public string Event { get; set; }
public string FileType { get; set; }
}
The Company, Department and Employee classes basically contains Id(int), Name and Description (both strings), A string property for the Foreign Key description (Department which an Employee belongs to, for an example), and a Selected(boolean) properties. For each one I developed an EditorTemplate like this one:
// ~\Views\Shared\EditorTemplates\Employee.cshtml
#model Model.ViewModels.Employee
<tr id="#Model.Id">
<td>
#Html.CheckBoxFor(m => m.Selected, new { #class = "selectEmp" })
// I use this for filtering values between tables, using javascript
// (i.e.: to show only Employees from a Department)
</td>
<td>
#Html.HiddenFor(m => m.Name)
#Html.DisplayFor(m => m.Name)
</td>
<td>
#Html.HiddenFor(m => m.Department)
#Html.DisplayFor(m => m.Department, new { #class = "fk" })
</td>
</tr>
When I retrieve those lists from DB, they contain several objects (i.e.: Employee list have 10 objects), but when I post back the viewmodel with the selections, the Employee list contains only one object. This don't occur with Company and Department lists. Since I don't remove anything from the lists (I just use the "Selected" property to properly filtering them), where I am getting wrong?
(I've googled through this, this, and this articles, but I still not get it)
EDIT - JS Scripts used for filtering (with jQuery DataTables)
function GetColumn(class) {
if (class == '.selectCom') {
return 1;
}
else {
return 2;
}
};
//this function returns a keyword list for filtering
function GetSearchArray(tbl, searchClass) {
if (searchClass === null || searchClass === undefined) {
searchClass = '';
return new Array();
}
else {
var searchArray = new Array();
$(searchClass).toArray().forEach(function (item, index, array) {
if ($(item).is(':checked')) {
tbl.settings.currentColumn = GetColumn(searchClass);
searchArray.push($(item).parents('tr')[0].cells[1].innerHTML);
}
});
return searchArray;
}
};
// this function effectively filters a child table based on what is selected on parent table
function TableFilter(parentTableId, parentClassName, childTableId, childClassName) {
var tbl = $(childTableId).dataTable();
var keywords = GetSearchArray($(parentTableId).DataTable(), parentClassName);
var filter = '';
keywords.forEach(function (item, index, array) {
filter = (filter !== '') ? filter + '|' + item : item;
});
tbl.fnFilter(filter, GetColumn(childClassName), true, false, false, true);
};
// These events call the filtering function and clear what is selected on child tables.
$('.selectCom').change(function () {
TableFilter('#tbCom', '.selectCom', '#tbDep', '.selectDep');
$('.selectDep').prop('checked', false);
$('.selectEmp').prop('checked', false);
});
$('.selectDep').change(function () {
TableFilter('#tbDep', '.selectDep', '#tbEmp', '.selectEmp');
$('.selectEmp').prop('checked', false);
});
Thank you in Advance.
You are going to have to do something like this Example of Posting Collections to Controller.
Your editor template will have to be a foreach() loop, and the iterate through the loop and manually set the name and id of each element. So , something like the below.
for(int i = 0; i < Employees.Count ; i++){
<input type="textbox" name="Employees[i].Name" id="Employees[i].Name" />
...
...
// if Employees is a list and contains a list as a property then
for(int x =0; x < Employees[i].Departments.Count; x++){
<input type="textbox" name="Employees[i].Departments[x]" ...
I know departments isn't a list, but that is an example of how you do it.
Here, the problem was on client-side, since "fnFilter" function from DataTables.Net removes objects effectively from those lists inside my viewmodel. This way, I think that the "fnFilter" mess around with EditorTemplate's indexes (and scramble them), and on POST, ASP.NET MVC doesn't know how to reconstruct those indexes.
So what should we do? I've changed my filtering function on client-side just to hide filtered rows (adding a "display:none" class for each filtered row). So, when we post back the form, the list remains intact, including selected values. The indexes were perfect.
Thanks to #StephenMuecke who gave me the clue.
I am newbie in MVC Razor and I want to implement validation message on textboxes. Here I'm creating some textbox dynamically as follows:
View Code:
foreach (var items in (IEnumerable<System.Data.DataRow>)Model.UsersOfList)
{
#Html.TextBoxFor(m => m.LoginNameOfLoginInfoTab, new { #class = "textBox_LoginInfoAndPermission", #value = (Model.LoginNameOfLoginInfoTab = items["UserName"].ToString()), #id = ("txtUserLoginName" + Model.UsernameOfLoginInfoTab.Trim()) })
#Html.ValidationMessageFor(m => m.LoginNameOfLoginInfoTab, null, new { #class = "ErrorMessage" })
#Html.TextBoxFor(m => m.UsernameOfLoginInfoTab, new { #class = "textBox_LoginInfoAndPermission", #value = (Model.UsernameOfLoginInfoTab = items["FirstName"].ToString()), #id = ("txtUserName" + Model.UsernameOfLoginInfoTab.Trim()) })
#Html.ValidationMessageFor(m => m.UsernameOfLoginInfoTab, null, new { #class = "ErrorMessage" })
}
in the module I've written code for validation as follows:
[Required (ErrorMessage="*")]
public string UsernameOfLoginInfoTab
{
get;
set;
}
[Required(ErrorMessage = "*")]
public string LoginNameOfLoginInfoTab
{
get;
set;
}
Now when all textboxes has been created and when one validation message is displaying for first loop iteration textbox then it will automatically displaying in front of another textbox too which is created on second loop iteration.
Please tell me whats going wrong.
The problem is because the expression you're using in TextBoxFor and ValidationMessageFor, which is used by MVC to create a string name for the field and look up validation messages from ModelState, is always the same throughout the iteration of the loop.
Your approach here seems a bit flawed, so my answer is more comprehensive.
1) Make view models that structurally represent the info you are trying to display.
Fix your view models:
public class UserInfoViewModel
{
[Required (ErrorMessage="*")]
public string UserName { get; set; }
[Required(ErrorMessage = "*")]
public string LoginName { get; set; }
}
// I don't know if you actually need this or not, but your existing model may contain additional properties relevant to the view that I don't know about, so I'll keep it.
public class ListOfUsersViewModel
{
public IList<UserInfoViewModel> UsersOfList { get; set; }
}
Fix your action (I'm making this up here to illustrate a point):
public ActionResult ListOfUsers()
{
var users = GetUserDataRows(); // gets your collection of DataRows
var model = new ListOfUsersViewModel
{
UsersOfList = users.Select(row = new UserViewModel { UserName = row["FirstName"], LoginName = row["UserName"] }).ToList()
};
return View(model);
}
2) Now you can iterate through users in your view and create proper fields with validation messages.
Let's call this view ListOfUsers.cshtml. Include whatever other things you need in your view, but use a for loop instead.
#using(Html.BeginForm("ListOfUsers"))
{
<ul>
#for (var i = 0; i < Model.UsersOfList.Count; i++)
{
<li>
#Html.TextBoxFor(m.UsersOfList[i].LoginName, new {#class="textbox_LoginInfoAndPermission"})
#Html.ValidationMessageFor(m => m.UsersOfList[i].LoginName)
#Html.TextBoxFor(m.UsersOfList[i].UserName, new {#class="textbox_LoginInfoAndPermission"})
#Html.ValidationMessageFor(m => m.UsersOfList[i].UserName)
</li>
}
</ul>
<button type="submit">Submit changes</button>
}
This will result in HTML like this for each item (0 in the name and id will be the index of the user in the collection):
<li>
<input type="text" id="UsersOfList_0_LoginName" name="UsersOfList[0].LoginName" value="..." />
<span class="field-validation-valid" data-valmsg-for="UsersOfList_0_LoginName" ... ></span>
<input type="text" id="UsersOfList_0_UserName" name="UsersOfList[0].UserName" value="..." />
<span class="field-validation-valid" data-valmsg-for="UsersOfList_0_UserName" ... ></span>
</li>
3) Create an action to receive submitted changes. This action will automatically bind the submitted values to the model argument, and do validation for you. All you need to do is check ModelState.IsValid.
[HttpPost, ActionName("ListOfUsers")]
public ActionResult ListOfUsersPost(ListOfUsersViewModel model)
{
// at this point, model will be instantiated, complete with UsersOfList with values submitted by the user
if (ModelState.IsValid) // check to see if any users are missing required fields. if not...
{
// save the submitted changes, then redirect to a success page or whatever, like I do below
return RedirectToAction("UsersUpdated");
}
// if ModelState.IsValid is false, a required field or other validation failed. Just return the model and reuse the ListOfUsers view. Doing this will keep the values the user submitted, but also include the validation error messages so they can correct their errors and try submitting again
return View("ListOfUsers", model);
}