I generate a number of textbox based on the number of element in a list.
The model :
public class MyModel
{
public List<Language> Languages { get; set; }
public string Code { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public bool IsDefault { get; set; }
}
The view :
#model MyModel
<form id="formDetail">
#Html.TextBoxFor(m => m.Code)
#foreach (var item in Model.Language)
{
<input type="text" id="#item.Code"/> //the code are : FR, EN, GE, ...
}
</form>
I post the form (POST) with Ajax.
Controller :
[HttpPost]
public ActionResult Save(MyModel myModel)
{
..
}
The number of textbox can be different depending of the number of language in the Language list. Could you tell me how get the value of these textboxes in the controller
Thanks,
Replace:
#foreach (var item in Model.Language)
{
<input type="text" id="#item.Code"/> //the code are : FR, EN, GE, ...
}
with:
#for (var i = 0; i < Model.Language.Count; i++)
{
#Html.HiddenFor(x => x.Language[i].Id)
#Html.HiddenFor(x => x.Language[i].Name)
#Html.HiddenFor(x => x.Language[i].IsDefault)
#Html.EditorFor(x => x.Language[i].Code)
}
and the model binder's gonna take care for the rest.
You have to use an indexed for loop to render the corresponding values for the nameattribute for the inputs, the TextboxFor helper and a hidden field, that sends the value for the Code property:
#for (int i=0; i<Model.Language.Count; i++)
{
#Html.TextboxFor(m => Model.Language[i].Name)
#Html.HiddenFor(m => Model.Language[i].Code)
}
Related
I have a view with a list of checkboxes. It display fine, but on Post, the count=0 for my list. I've seen a bunch of post on SO that all say the same thing, which I've trie, but it's not working, so I must be missing something.
My ViewModels:
public class ParentViewModel
{
public ParentViewModel()
{
ClassOfTradeList = new List<CheckBoxListItem>();
}
public string Name { get; set; }
public List<CheckBoxListItem> ClassOfTradeList { get; set; }
}
public class CheckBoxListItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
Then my View:
#model app.Models.ViewModels.ParentViewModel
// other parent fields
#for (int i = 0; i < Model.ClassOfTradeList.Count; i++)
{
#Html.CheckBoxFor(m => m.ClassOfTradeList[i].IsSelected)
#Html.HiddenFor(m => m.ClassOfTradeList[i].Id)
#Html.LabelFor(m => m.ClassOfTradeList[i].IsSelected, Model.ClassOfTradeList[i].Name)
<br />
}
My Post in the controller:
public ActionResult ParentReturn(ParentViewModel model)
{
}
My Get:
[HttpGet]
public ActionResult Parent()
{
var model = new ParentViewModel();
//fill in properties here
model.ClassOfTradeList = ClassOfTradeList();
return View(model);
}
private List<CheckBoxListItem> ClassOfTradeList()
{
var classofTrades = _tradeRepo.GetAll().Where(c => c.IsDeleted == false).ToList();
var classOfTradeList = new List<CheckBoxListItem>();
classOfTradeList = classofTrades.Select(c => new CheckBoxListItem
{
Name = c.Name,
Id = c.Id
}).ToList();
return classOfTradeList;
}
So when I get back to the controller, the model.ClassOfTrades.Count =0. What am I missing?
I figured it out. When I first was trying to fix this, I put:
#Html.HiddenFor(m => m.ClassOfTradeList)
in the beginning of the View. It was overriding my hiddenfor of the id later on in the view.
Thanks all for your help. So removing that and keeping the code I have posted made it work.
You should probably use "Model.ClassOfTradeList[i].Name" instead of Id.
#using (Html.BeginForm("ParentReturn", "Home", FormMethod.Post))
{
for (int i = 0; i < Model.ClassOfTradeList.Count; i++)
{
#Html.CheckBoxFor(model => Model.ClassOfTradeList[i].IsSelected);
#Html.HiddenFor(model => Model.ClassOfTradeList[i].Name);
#Html.LabelFor(model => Model.ClassOfTradeList[i].IsSelected, Model.ClassOfTradeList[i].Name);
<br />
}
<input type="submit" value="click" />
}
https://dotnetfiddle.net/C0trTW
I am creating configurable forms in MVC which will contain dynamic controls base off of this SO post. The controls are all built from my base ControlViewModel which just contains properties for all of the controls
public abstract class ControlViewModel
{
public abstract string Type { get; }
public bool Visible { get; set; }
public string Label { get; set; }
public string Name { get; set; }
}
Each control type is defined seperately by inheriting ControlViewModel
public class TextBoxViewModel : ControlViewModel
{
public override string Type
{
get { return "textbox"; }
}
public string Value { get; set; }
}
I have text boxes, check boxes, and drop downs all defined in a similar manner. The issue I am having is when the controls are displayed on the page, their name and id attributes are not rendering as expected. In my controller I have
public ActionResult Index()
{
var model = new MyViewModel
{
Controls = new ControlViewModel[]
{
new TextBoxViewModel
{
Visible = true,
Label = "label 1",
Name = "TextBox1",
Value = "value of textbox"
}
}
}
return View(model)
}
In my Index.cshtml I render each control like so:
#model DynamicForms.Models.MyViewModel
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Controls.Length; i++)
{
if (Model.Controls[i].Visible)
{
<div>
#Html.HiddenFor(x => x.Controls[i].Type)
#Html.HiddenFor(x => x.Controls[i].Name)
#Html.EditorFor(x => x.Controls[i])
</div>
}
}
<input type="submit" value="OK" />
}
The editor just renders the control and the label
#model DynamicForms.Models.TextBoxViewModel
#Html.LabelFor(x => x.Value, Model.Label)
#Html.TextBoxFor(x => x.Value)
The issue is that when the page renders, the name and id attributes don't render as the actual string values, but instead as the type
<div>
<input id="Controls_0__Type" name="Controls[0].Type" type="hidden" value="textbox">
<input id="Controls_0__Name" name="Controls[0].Name" type="hidden" value="TextBox1">
<label for="Controls_0__Value">label 1</label>
Does anyone know how I can populate the name and id attributes correctly here?
I have the following cshtml form
#using (Html.BeginForm(Html.BeginForm("Create", "UserRole", Model, FormMethod.Post)))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Role</legend>
<div class="editor-label">
#Html.Label(Model.User.UserName)
</div>
<div class="editor-field">
#Html.CheckBoxList(Model.CheckboxList)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And I wish to get the Model.CheckboxList selected Items in my action.
I have the following Create Action in my Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(UserRoleViewModel userRoleViewModel)
{
if (ModelState.IsValid)
{
//_context.Role.Add(role);
//_context.SaveChanges();
//return RedirectToAction("Index");
}
return View(viewModel);
}
However the viewModel.CheckboxList is 0.
How can I pass the selected values of the checkboxlist, and also the Model.User to the Controller Action?
My ViewModel looks like this :-
public User User { get; set; }
public IEnumerable<Role> RoleList { get; set; }
public List<UserRoleViewModel> UserList { get; set; }
public IEnumerable<SelectListItem> CheckboxList { get; set; }
public UserRoleViewModel()
{
}
public UserRoleViewModel(User user, IEnumerable<Role> roleList )
{
User = user;
RoleList = roleList;
}
Thanks for your help and time!
UPDATE ----------- After reading this post enter link description here, I tried to adapt my code to follow the example, but I am still finding problems with this updated code.
Now I have the following :-
cshtml :-
#model IEnumerable<MvcMembership.ViewModels.RoleCheckboxListViewModel>
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
Views/Role/EditorTemplates/RoleCheckboxListViewModel.cshtml
#model MvcMembership.ViewModels.RoleCheckboxListViewModel
#Html.HiddenFor(x => x.RoleId)
#Html.HiddenFor(x => x.RoleName)
<div>
#Html.CheckBoxFor(x => x.Checked)
#Html.LabelFor(x => x.Checked, Model.RoleName)
</div>
ViewModels :-
public class RoleCheckboxListViewModel
{
public string RoleId { get; set; }
public string RoleName { get; set; }
public bool Checked { get; set; }
}
and the controller action is as follows :-
public ActionResult Create(int? uid)
{
var checkBoxList = new[]
{
new RoleCheckboxListViewModel() {
RoleId = "1", Checked = true, RoleName = "item 1" },
new RoleCheckboxListViewModel() {
RoleId = "2", Checked = true, RoleName = "item 2" },
new RoleCheckboxListViewModel() {
RoleId = "3", Checked = true, RoleName = "item 3" },
};
return View(checkBoxList);
}
The problem I have now is that on the Create.cshtml. I cannot see the checkboxlist, but only 123 displayed as well as the OK button.
Any help would be very much appreciated cause I am at a dead end at the moment.
I've accomplished this with the following parts:
1) A view model for the child element that adds the bool property that will represent whether or not the checkbox is checked in the View later... ie:
public class CategoryViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public bool Assigned { get; set; }
}
2) A view model for the parent element that adds a collection property for this new child element view model, ie:
public class ManufacturerViewModel
{
public Manufacturer Manufacturer { get; set; }
public IList<CategoryViewModel> Categories { get; set; }
public ManufacturerViewModel()
{
Categories = new List<CategoryViewModel>();
}
}
3) A service layer method for getting a list of all child elements, while also setting the bool property for each ("Assigned" in my example). To be used by your controller.
public IList<CategoryViewModel> GetCategoryAssignments(Manufacturer mfr)
{
var categories = new List<CategoryViewModel>();
foreach (var category in GetCategories())
{
categories.Add(new CategoryViewModel
{
ID = category.ID,
Name = category.Name,
Assigned = mfr.Categories.Select(c => c.ID).Contains(category.ID)
});
}
return categories;
}
4) A method for updating the parent item's collection based on your checkboxlist selections. To be used by your controller.
public void UpdateCategories(string[] selectedCategories, ManufacturerViewModel form)
{
if (selectedCategories == null)
selectedCategories = new string[] { };
var selectedIds = selectedCategories.Select(c => int.Parse(c)).ToList();
var assignedIds = form.Manufacturer.Categories.Select(c => c.ID).ToList();
foreach (var category in GetCategories())
{
if (selectedIds.Contains(category.ID))
{
if (!assignedIds.Contains(category.ID))
form.Manufacturer.Categories.Add(category);
}
else
{
if (assignedIds.Contains(category.ID))
form.Manufacturer.Categories.Remove(category);
}
}
}
5) Modifications to your Create/Edit view. ie:
#Html.EditorFor(model => model.Categories)
You must also add this so that the original assigned values are included in post data. You'll have to add a HiddenFor for each property that you have set as Required through validation.
for (int i = 0; i < Model.Manufacturer.Categories.Count; i++)
{
#Html.HiddenFor(model => model.Manufacturer.Categories[i].ID);
#Html.HiddenFor(model => model.Manufacturer.Categories[i].Name);
}
6) And finally, a new EditorTemplate for your child view model element. ie:
#model YourProject.ViewModels.CategoryViewModel
<li>
<input type="checkbox"
id="#string.Format("cb{0}{1}", #Model.Name, #Model.ID)"
name="selectedCategories" //Notice this name corresponds to string[] selectedCategories so that it can be extracted from the post data
value="#Model.ID"
#(Html.Raw(Model.Assigned ? "checked=\"checked\"" : "")) />
<label for="#string.Format("cb{0}{1}", #Model.Name, #Model.ID)">#Model.Name</label>
#Html.HiddenFor(model => model.ID)
</li>
Hopefully my own application gives you a better idea of how to solve this issue.
Store your selected value into the variable as follows, and pass it to an hidden field, then you can access it easily
var modelSelected = document.getElementById("modelName");
document.getElementById('selectedModel').value =
modelSelected.options[modelSelected.selectedIndex].text;
<input id="selectedModel" name="selectedModel" type="hidden" runat="server" />
I have a simple quiz model, and I am trying to let the user select Correct Answer/Alternative answer from two radio buttons, grouped , in a strongly typed view. But the lambda expressions I use aren't working. I get two blank radio buttons. I have looked at several questions here, and online but my model is an IList<>, and I can't find a suitable example. All examples I found work with a non-IList<>.
This is my Model
Model:
public partial class Question
{
public int QuestionID { get; set; }
public string QuestionBody { get; set; }
public string CorrectAnswer { get; set; }
public string AlternativeAnswer { get; set; }
}
My Controller
public ActionResult Index()
{
QuizSimpleEntities quizEntities = new QuizSimpleEntities();
var questions = from p in quizEntities.Questions
select p;
return View(questions.ToList());
}
My Model:
#model IList<Quiz.Models.Question>
<h2>Welcome to the Quiz</h2>
#Html.BeginForm(method:FormMethod.Post,controllerName:"Home",actionName:"index")
{
#foreach (var questions in Model)
{
<p>#questions.QuestionBody</p>
#* How to display the CorrectAnswer and AlternativeAnswer
as two radio buttons grouped here? I will be posting the selected value back
}
}
Thank you
You need to have a property on your view model that will hold the selected answer when the form is posted:
public partial class Question
{
public int QuestionID { get; set; }
public string QuestionBody { get; set; }
public string CorrectAnswer { get; set; }
public string AlternativeAnswer { get; set; }
public string SelectedAnswer { get; set; }
}
and then simply loop through the elements of your model and generated the desired markup:
#model IList<Quiz.Models.Question>
<h2>Welcome to the Quiz</h2>
#Html.BeginForm( method:FormMethod.Post, controllerName:"Home", actionName:"index")
{
#for (var i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(x => x[i].QuestionID)
<fieldset>
<legend>
#Html.DisplayFor(x => x[i].QuestionBody)
</legend>
<ul>
<li>
#Html.HiddenFor(x => x[i].CorrectAnswer)
#Html.RadioButtonFor(x => x[i].SelectedAnswer, Model[i].CorrectAnswer)
#Html.DisplayFor(x => x[i].CorrectAnswer)
</li>
<li>
#Html.HiddenFor(x => x[i].AlternativeAnswer)
#Html.RadioButtonFor(x => x[i].SelectedAnswer, Model[i].AlternativeAnswer)
#Html.DisplayFor(x => x[i].AlternativeAnswer)
</li>
</ul>
</fieldset>
}
<button type="submit">OK</button>
}
NOTE: When the form is submitted the POST action could take an IList<Question> model where you will have the answers for each question (in the SelectedAnswer property).
I am currently working on an Edit function for my project and I cannot seem to get the View Model right to allow it to be passed back into the Controller from the View. The structure of the View Model is as such:
public class CreateUserViewModel : ICreateUserViewModel
{
#region Properties
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string SelectedUserType { get; set; }
public List<ICreateUserItemViewModel> UserTypes { get; set; }
public List<ICreateUserItemViewModel> Products { get; set; }
public List<ICreateUserItemViewModel> Languages { get; set; }
#endregion
#region Constructor
public CreateUserViewModel()
{
}
public CreateUserViewModel(List<Product> products, List<Language> languages)
{
Products = new List<ICreateUserItemViewModel>();
foreach (var prod in products)
{
var prodVM = new CreateUserItemViewModel
{
Name = prod.Name,
IsSelected = false,
ID = (int)prod.ID
};
Products.Add(prodVM);
}
Languages = new List<ICreateUserItemViewModel>();
foreach (var lang in languages)
{
var langVM = new CreateUserItemViewModel
{
Name = lang.Name,
IsSelected = false,
ID = (int)lang.ID
};
Languages.Add(langVM);
}
}
#endregion
}
The subclass ViewModel CreateUserItemViewModel:
public class CreateUserItemViewModel : ICreateUserItemViewModel
{
public string Name { get; set; }
public int ID { get; set; }
public bool IsSelected { get; set; }
}
I want this subclass to be represented in the view as a checkbox, so the user can choose to include it or not.
User Controller:
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
if (ModelState.IsValid)
{
User newUser = new User();
newUser.UserName = model.UserName;
newUser.Password = model.Password;
newUser.Email = model.Email;
newUser.Products = model.Products; //Always NULL
When I put a break point on this part of the application the properties for username password and email are populated but Products and Languages are empty. I used Fiddler2 to watch what was being passed into the controller and this was the output:
UserName=asdasdsdasd&Password=asd&Email=asD%40asd.com&type.IsSelected=Admin
&prod.ID=1&prod.IsSelected=true&prod.IsSelected=false
&prod.ID=2&prod.IsSelected=false
&prod.ID=3&prod.IsSelected=false
&prod.ID=4&prod.IsSelected=false
&lang.ID=1&lang.IsSelected=true&lang.IsSelected=false
&lang.ID=2&lang.IsSelected=true&lang.IsSelected=false
&lang.ID=3&lang.IsSelected=false
&lang.ID=4&lang.IsSelected=false
&lang.ID=5&lang.IsSelected=false
&lang.ID=6&lang.IsSelected=false
&lang.ID=7&lang.IsSelected=false
&lang.ID=8&lang.IsSelected=false
&lang.ID=9&lang.IsSelected=false
&lang.ID=10&lang.IsSelected=false
&lang.ID=11&lang.IsSelected=false
&lang.ID=12&lang.IsSelected=false
&lang.ID=13&lang.IsSelected=false
&lang.ID=14&lang.IsSelected=false
&lang.ID=15&lang.IsSelected=false
&lang.ID=16&lang.IsSelected=false
&lang.ID=17&lang.IsSelected=false
&lang.ID=18&lang.IsSelected=false
&lang.ID=19&lang.IsSelected=false
A nugget of the code for the Create View is:
<div class="editor-label">
#Html.Label("User Products:")
</div>
<div class="editor-field">
#foreach (var prod in Model.Products)
{
#Html.Label(prod.Name)
#Html.HiddenFor(x => prod.ID)
#Html.CheckBoxFor(x => prod.IsSelected, new { name = prod.Name })
}
</div>
<div class="editor-label">
#Html.Label("User Languages:")
</div>
<div class="editor-field">
#foreach (var lang in Model.Languages)
{
#Html.Label(lang.Name)
#Html.HiddenFor(x => lang.ID)
#Html.CheckBoxFor(x => lang.IsSelected, new { name = lang.Name })
}
</div>
<p>
<input type="submit" value="Create" />
</p>
I have been working on this for quite a while and I am beginning to think what I am trying to do is not possible. I want it to return the CreateUserViewModel fully populated with all the values that the user has selected, but I just do not know how to achieve this.
Any ideas?
Use a for loop instead of a foreach.
#for (int i=0; i < Model.Products.Count; i++)
{
#Html.Label(prod.Name)
#Html.HiddenFor(model => model.Products[i].ID)
#Html.CheckBoxFor(model => model.Products[i].IsSelected, new { name = prod.Name })
}
Your property expression needs to contain enough information for the model binder to figure out how to bind the POST values to the model.
You could also try creating an Editor Template for ICreateUserItemViewModel and then changing your markup to the following.
#Html.EditorFor(model => model.Products)
As already mentioned in posted answer, it can be done by applying indexes but if the elements are non-sequential refer below article
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Also refer below one for introduction to "Model Binding"
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Adding to the suggestion list, consider removing the CreateUserViewModel(List products, List languages) from the viewmodel. ViewModel should contain only properties and place additional logic (i.e, attaching UserTypes, etc., in your case) inside the controller.