Is it correct a model?
public class NewForm
{
public string[] Field { get; set; }
public bool[] Check { get; set; }
}
for such a VIEW:
#Html.TextAreaFor(model => model.Field)
#Html.TextAreaFor(model => model.Field)
#Html.TextAreaFor(model => model.Field)
#Html.CheckBoxFor(model => model.Check)
#Html.CheckBoxFor(model => model.Check)
Or is there a better way to create fields of the same name?
In Controller displays only the first value. But i need all
[HttpPost]
public ActionResult Edit(NewForm model)
{
Response.Write(model.Field);
Response.Write(model.Check);
}
Fields may be an indefinite number due to the fact that by clicking on the button JavaScript adds a new field of the same name with the same name
It sounds like you want to submit multiple instances of your model back to the controller.
You could do something like this. My example will submit 10 instances of Field back to your controller.
View:
#using (Html.BeginForm())
{
<div>
#for(int i = 0; i<10; i++)
{
<div>#Html.TextBox("items[" + i + "].Field", "", new { id = "items[" + i + "].Field", placeholder = "Enter Text..." })</div>
#Html.Hidden("items.Index", i)
}
</div>
<input type="submit" value="Submit" />
}
Class:
public class MyClass
{
public string Field {get;set;}
}
Controller Method:
[HttpPost]
public ActionResult ActionName(List<MyClass> items)
{
//...do stuff
}
Obviously you could also add your checkbox into the model and form too in order to submit many of those.
Why you want to be the same name for the fields, each field has proper name
public class NewForm
{
public string FirstField { get; set; }
public string Field { get; set; }
public bool Check { get; set; }
}
VIEW
#Html.TextAreaFor(model => model.FirstField)
#Html.TextAreaFor(model => model.Field)
#Html.CheckBoxFor(model => model.Check)
ee
[HttpPost]
public ActionResult Edit(NewForm model)
{
Response.Write(model.FirstField);
Response.Write(model.Field);
Response.Write(model.Check);
}
No, it isn't. In the model you defined, you have created two different arrays: The first property Field is an array of strings and the second property Check an array of bools. putting the [] after a type indicates an array.
If you have an unknown number of what I'll call "mini forms" and the number of these is decided by the user via the UI, then you should create a view model to represent this mini form, and a container view model to house it and any other properties your view will need.
For example:
public class MiniFormViewModel
{
public string MyInput { get; set; }
public bool MyCheck { get; set; }
}
then in your container view model:
public class ContainerViewModel
{
public IEnumerable<MiniFormViewModel> MiniForms { get; set; }
//Any other properties you need on the view that will occur a single time
}
Now, in the JS you'll need to add some manipulation in order to do this:
function getViewModel() {
//You'll have to decide how you want to get the values of the mini form's fields. Perhaps you might even have a function to supply these values. Up to you.
return {
MiniForms: [{
MyInput: '', //value from the first "mini form' string field
Mycheck: false //value from the first "mini-form" bool field
},
{
MyInput: '', //value from the second"mini form' string field
Mycheck: false //value from the second"mini-form" bool field
}
]
}
}
Then you'll need to post this back to the server. I'll demonstrate how to do this via the built in JS Fetch function:
fetch(yourUrlForTheControllerAction,
{
method: 'post',
body: JSON.stringify(getViewModel()),
headers: {
'content-type': 'application/json; charset=UTF-8'
}
})
And then blammo, you should be good to go. I excluded the part of dynamically adding the mini form fields because it sounds like you have a solution for that already.
Related
Prefix: I don't like this approach, I think that the Bind attribute is clunky. That said, I have a complex model. It is in an ASP.NET MVC razor view that uses a partial view.
Ideally I would like to just have the partial view accept a DTO that has only the fields that I would like to bind. For reasons I can't control I don't have that option.
However I have the ability to modify the controller. So, I am trying to use the Bind attribute to only allow the properties of the child that I would like to bind.
Here are the parent and child model classes:
public class Parent
{
public Menu FullMenu { get; set; }
public KidDTO SubDTO { get; set; }
}
public class KidDTO
{
public string Name { get; set;}
public string Date { get; set;}
public string Addr { get; set;}
}
Razor parent view (again I would prefer to only pass the smaller DTO that will be used... but I don't have that option):
#model Core.ViewModels.Parent
//Other stuff here
#{ Html.RenderPartial("_CreateEdit", Model);}
Here's the child view:
#model Core.ViewModels.Parent
#using (Ajax.BeginForm("CreateEdit", "Child", new AjaxOptions { HttpMethod = "POST"}))
{
<div class="form-horizontal">
<hr />
<div class="form-group row">
<div class="col-md-10">
#Html.EditorFor(model => model.SubDTO.Name)
</div>
</div>
<input type="button" value="Submit" />
</div>
}
Controller:
[HttpPost]
public async Task<ActionResult> CreateEdit([Bind(Include="SubDTO.Name")] Parent model)
{
// check if its Null or Not
var IsItNull = model.SubDTO.Name; //<--model.SubDTO.Name is always Null. ?
return RedirectToAction($"{ControllerEntity}Manager");
}
So, in short If I Bind to the SubDTO by iteslf it has values, but that then allows for binding to the other attributes of the SubModel, which is what I'm trying to avoid?
I was Not able to figure how to get the Include to work with Child Models properties. I could only bind the entire child or nothing.. However I was get it to work using the [Bind(Prefix="")] in the controller like so.
Controller.
[HttpPost]
public async Task<ActionResult> CreateEdit([Bind(Prefix="SubDTO.Name")] string childName)
{
// check if its Null or Not
var IsItNull = childName //<--UGLY but Appears to work, This is not Ideal.
return RedirectToAction($"{ControllerEntity}Manager");
}
Like the comments above alludes, I do not consider this to be the best solution for this common situation, but based on my restrictions it currently the only option that I have available. This can get very very ugly with a lot of properties.
Try changing your model to explicitly set what you can bind to at the class level.
public class Parent
{
public Menu FullMenu { get; set; }
public KidDTO SubDTO { get; set; }
}
[Bind(Include = "Name")]
public class KidDTO
{
public string Name { get; set;}
public string Date { get; set;}
public string Addr { get; set;}
}
Controller
[HttpPost]
public async Task<ActionResult> CreateEdit(Parent model)
{
// check if its Null or Not
var IsItNull = model.SubDTO.Name;
return RedirectToAction($"{ControllerEntity}Manager");
}
Form Collection Post
[HttpPost]
public async Task<ActionResult> CreateEdit(FormCollection collection)
{
var subDTO = new KidDTO()
{
Name = collection["SubDTO.Name"]
};
// check if its Null or Not
var IsItNull = subDTO.Name;
return RedirectToAction($"{ControllerEntity}Manager");
}
cshtml change type button to submit
<input type="submit" value="Submit" />
To keep things simple and clean, try creating a separate model just with the Name property. The drawback using explicit [Bind] is that you can now only bind to the Name using the default Model binder so why not just create a new ViewModel to begin with?
I have a form that loads some Partial Views dinamically and one of these Partial Views will load multiple dropdownlists in the screen.
I have a ViewModel (principal): used in the main view
public class CupomFiscalDetalhesViewModel
{
//some properties
public IEnumerable<CupomItensViewModel> CupomItens { get; set; }
}
An intermediate ViewModel: the view model of the partial view:
public class CupomItensViewModel
{
public IEnumerable<TabelaPrecoViewModel> TabelasPreco { get; set; }
public TabelaPrecoViewModel TabelaPrecoSelecionada { get; set; }
}
Where TabelaPrecos is holding the values that I want to show in the DropDownList. and TabelaPrecoSelecionada will hold the selected value.
In the Controller, I'm used to put the values of an IEnumerable into a ViewBag, and use this ViewBag to generate the dropdownlist in the HTML, like this:
ViewBag.TabelaPrecoSelecionada = new SelectList
(
detalhesCupomFiscal.CupomItens.FirstOrDefault().TabelasPreco,
"IdTabela",
"NomeTabela"
);
But I have no idea how to generate multiple dropdowns for each option of CupomItensViewModel, without passing the id of the selected value of each dropdownlist to the controller action (by parameter).
In the Html, I use: but would need to change the name to get binding workin somehow.
#Html.DropDownList("TabelaPrecoSelecionada",(IEnumerable<SelectListItem>)ViewBag.TabelaPrecoSelecionada,
new { #class = "form-control dropdown" })
Does anyone has an Idea how to accomplish it?
I haven't test this but I would maybe create the select list inside your CupomItensViewModel
using System.Linq;
public class CupomItensViewModel
{
public IEnumerable<TabelaPrecoViewModel> TabelasPreco { get; set; }
public TabelaPrecoViewModel TabelaPrecoSelecionada { get; set; }
public IEnumerable<SelectListItem> TabelasPrecoSelectList
{
get
{
return TabelasPreco.Select(x => new SelectListItem()
{
Value = x.IdTabela
Text = x.NomeTabela
Selected = TabelaPrecoSelecionada.IdTabela
}
}
}
}
And Inside your view
#foreach(var item in Model.CupomItens)
{
#Html.DropDownList("TabelaPrecoSelecionada", item.TabelasPrecoSelectList, new { #class = "form-control dropdown" })
}
But if these dropdowns aren't going to be next to each other, I would make
public IEnumerable<CupomItensViewModel> CupomItens { get; set; }
List instead and using index to identify them. CupomItens[x]
Just my 2 cent without checking if it works. Hopefully it helps.
So I am currently studying and analyzing the use of ViewModels.
In my Application (a so called "Restaurant") I want the ability for my "users" to create a menu.
When they want to create a menu: They can choose the name + the amount of persons that can join the menu. BUT also, they can add an amount of dishes that are already in the restaurant. This will be in the style of checkboxes and an 'Create'-Button at the end.
This means I had to use a ViewModel. I am currently trying to give the possibility to add a list of dishes to a menu for the creation. But I'm stuck at the for loop, used to loop through the dishes. Or better, I'm stuck at the whole concept:
What is the best way to display all the already created dishes to the CreateMenu View? Is it still possible to loop through a ViewBag if I will add them in a ViewBag?
Lets say I successfully tried to do what I wanted to do. How would I create a new Menu based (or extracted?) from the ViewModel?
In my Code, please note that the Menu - Model cannot be changed really because I already use a list of Dishes from it (In another view, where I display all the menu's and their dishes).
also ignore the possibility of wrong names or spelling mistakes in data, since I translated everything from Flemish
Models
public class Menu
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0,10)]
public int AmountPersons { get; set; }
[Range(0,double.MaxValue)]
public double Price { get; set; }
public virtual List<Dish> Dishes { get; set; }
}
public class Dish
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public enum Types { VOORGERECHT, HOOFDGERECHT, DRANK, DESSERT}
public Types Type { get; set; }
public double Price { get; set; }
public virtual List<Menu> Menus { get; set; }
public virtual List<Table> Tables { get; set; }
//Checked used for the 'checkbox' in the CreateMenu-View
[NotMapped]
public bool Checked { get; set; }
}
public class MenuViewModel
{
public Menu Menu { get; set; }
public List<Dish> AddedDishes { get; set; }
}
Controller
public ActionResult CreateMenu( )
{
MenuViewModel gm = new MenuViewModel();
// Assign ALL already created dishes to the list that the user can choose.
// AddedDishes is wrong? ViewBag preferred?
gm.AddedDishes = db.Dishes.ToList();
return View(gm);
}
// Add the Menu to all the Menu's in the Database.
[HttpPost]
public ActionResult MenuAanmaken(MenuModel gm)
{
// code to save the menu with all the added dishes to the database
// Conflict!? Cannot convert the MenuViewModel to the Menu-model How do we need to extract the Menu and the AddedDishes list
// to a menu and save that one to the database?
db.Menus.Add(gm);
return View(gm);
}
View
#using VBExamen.Models
#model MenuViewModel
....
#Html.LabelFor(m => m.Menu.Name)
#Html.EditorFor(m => m.Menu.Name)
#Html.LabelFor(m => m.Menu.AmountPersons)
#Html.EditorFor(m => m.Menu.AmountPersons)
#for(int i = 0; i < Model.AddedDishes.Count; i++)
{
<tr>
<td>
#Html.DisplayFor( .Name)
#Html.HiddenFor(item => .Id)
#Html.CheckBoxFor(item => .Checked)
</td>
</tr>
}
E D I T E D _ U P D A T E (SEE BELOW)
Okay So I think I'm close now,
I edited my classes as the following:
public class MenuViewModel<T>
{
public Menu Menu { get; set; }
public List<T> DishList { get; set; }
public MenuViewModel()
{
this.Lijst = new List<T>();
}
}
Controller
public ActionResult CreateMenu(MenuViewModel<Dish> model )
{
model.DishList = db.Gerechten.ToList();
return View(model);
}
[HttpPost]
public ActionResult CreateMenu(MenuViewModel<Dish> model,List<Gerecht> SelectedList)
{
Menu t = new Menu();
t.Naam = gm.Menu.Naam;
t.AmountPersons = gm.Menu.AmountPersons;
t.Dishes = SelectedList;
db.Menus.Add(t);
return View("Menus", model);
}
View function creating list
#for (int i = 0; i < Model.DishList.Count(); i++)
{
<tr>
<td>
#Html.Label(Model.DishList[i].Naam)
<input type="hidden" name=#String.Format("DishList[{0}].Id", i) value=#Model.DishList.ElementAt(i).Id />
<input type="hidden" name=#String.Format("DishList[{0}].Name", i) value=#Model.DishList.ElementAt(i).Name />
<input type="checkbox" name=#String.Format("DishList[{0}].value", i) />
<input type="hidden" name=#String.Format("DishList[{0}].value", i) value="false" />
</td>
<br />
</tr>
}
I did this after watching about 10 tutorials about ViewModels, is my next approach better than the first one?
I think so because i get the following on my screen:
I was thinking what the next approach would be. I was thinking about comparing the 2 lists (1 of the viewmodel, 1 passed) and see the checkbox statuses?
UPDATE
After Stephen Muecke's answer I re-edited my code but found a problem that I can't seem to understand.
The answer says I should be in the position of a 1-to-many table in the form as a class:
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
However, what we've learned at school was, that if you create lists in the data-models of the entities, linked tables will be automatically generated in the Database. And that is exactly what my DB has done (without the creation of the MenuDish class):
MenuGerechts stands for MenuDish.
This is the automatically created table done by the entity framework.
That brings me to the following questions. I have re-edited the controller to the following:
[HttpPost]
public ActionResult MenuAanmaken(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
Menu menu = new Menu()
{
Naam = model.Name,
AantalPersonen = model.AmountPersons
};
foreach (int id in selectedDishes)
{
Dish g = db.Dishes.Find(id);
menu.Dishes.Add(g);
};
db.Menus.Add(menu);
db.SaveChanges();
return RedirectToAction("Menus", "Menu");
}
I get the Object reference not set to an instance of an object error and I'm understanding why ofcourse.
I have done the changes since the Data-Model Menu, already has a List of Dishes. But assuming the answer of S. Muecke, this isn't the correct way to solve this ViewModel since he proposes the use of a New Class (that is created to support the one-to-many relationship)?
This brings me to the conclusion of the following questions:
Why is it impossible or not-recommended to directly add the selected dishes to the menu instance?
Is it always needed to create the in between table 'MenuDish' in a Data-model?
Will the following code still work (showing the menu's and their dishes) after creating new Menu's?:
Controller:
public ActionResult Menus()
{
List<Menu> menus = db.Menus.ToList();
return View(menus);
}
View:
#model IEnumerable<VBExamen.Models.Menu>
#{
ViewBag.Title = "Menus";
}
<h2>Menus</h2>
<p>
#Html.ActionLink("Create New Menu", "CreateMenu")
</p>
#foreach (var item in Model)
{
<table>
<ul>
<p>#Html.DisplayFor(modelItem => item.Name)</p>
#foreach (var g in item.Dishes)
{
<li>
#Html.DisplayFor(modelItem => g.Name)
</li>
}
</ul>
</table>
}
Which outputs the following:
What would be good motivations to do this?
UPDATE 2
So I have included the following in my project:
** I have used the Table()- annotation to make it use the one that's already created**
**Model: **
[Table("MenuGerechts")]
public class MenuGerechts
{
[Key]
[ForeignKey("Menu")]
public virtual int? MenuId { get; set; }
public virtual Menu Menu { get; set; }
[ForeignKey("Dish")]
public virtual int? DishId { get; set; }
public virtual Dish Dish { get; set; }
}
I have then actually created new menus successfully! But when I go to the overview menu page (from the pic above), it only shows the Name of the menu, and not the list of meals that it includes.
The Database however didn't allow my MenuDish link table to be used for my newly created class (it created a new one, and renamed the old one with the 'old' menus with a '1' behind it:
Hence why I was asking my previous questions. Does this mean my whole approach to this exercise was wrong?
New Question:
My menuCreate ViewModel only works if i Select 1 dish? Why is this so? I get the following error The role 'MenuGerechts_Menu_Source' of the relationship 'VBExamen.Models.MenuGerechts_Menu' has multiplicity 1 or 0..1.
Firstly a view model should not contain properties which are data models. It should contains only properties which you display/edit in the view, and I recommend you read What is ViewModel in MVC?.
Based in the image of the form you have shown, your view models needs to be (display and validation attributes omitted for simplicity)
public class MenuVM
{
public int? ID { get; set; } // included so this can be used for editing as well as creating
public string Name { get; set; }
public int AmountPersons { get; set; }
public List<DishVM> Dishes { get; set; }
}
public class DishVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the controller GET method
public ActionResult CreateMenu( )
{
// Get all the dishes from the database
var dishes = db.Dishes; // modify to suit
// Initialize the view model
var model = new MenuVM()
{
Dishes = dishes.Select(x => new DishVM()
{
ID = x.Id,
Name = x.Name
}).ToList()
};
return View(model);
}
Then in the view (LabelFor() and ValidationFor() methods omitted for simplicity)
#model MenuVM
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.AmountPersons )
for(int i = 0; i < Model.Dishes.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Dishes[i].ID)
#Html.HiddenFor(m => m.Dishes[i].Name)
#Html.CheckBoxFor(m => m.Dishes[i].IsSelected)
#Html.LabelFor(m => m.Dishes[i].IsSelected, Model.Dishes[i].Name)
</div>
}
<input type="submit" value="Create" />
}
And finally the POST method will be
public ActionResult CreateMenu(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Initialize and save the Menu
Menu menu = new Menu()
{
Name = model.Name,
AmountPersons = model.AmountPersons
};
db.Menus.Add(menu);
db.SaveChanges(); // you now have the ID of the new menu
// Save the dishes associated with the menu
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
foreach(int id in selectedDishes)
{
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
}
db.SaveChanges(); // save the selected dishes
return RedirectToAction(...); // redirect somewhere
}
Side note: Remove the [NotMapped] public bool Checked { get; set; } property from your data model.
This is only the answer to your first question... I gotta get back to work :)
I strongly advise you to use Entity Framework for storing this information, as creating the data context, Initializer(Entity Framework Requirements) and View Model will allow you to scaffold everything in your project including controllers and views. This means you take the information from the ViewModel class rather than from the view bag.
Scaffolding means that Visual Studio will create all your code to CRUD(Create, Read, Update, Delete) Controllers and Views to allow this to happen. Freeing you from either 45 mins of furious typing or hours of banging your head against a wall.
So lets do this, First we create our own context class inheriting from DbContext (A part of Entity Framework):
public class MenuContext : DbContext {
public MenuContext() : base("MenuContext") {
}
The base referenced here specifies the name in your web.config file of your connection string which we will set up momentarily. Alternatively you can specify your connection string in place of the name.
public DbSet<MenuViewModel> Menus { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
The Initializer class we will set up next populates the database if the database does not already exist at the connection string location.
class MenuInitializer : CreateDatabaseIfNotExists<MenuContext> {
protected override void Seed(MenuContext context) {
// This sets up your database to populate with whatever you type into this method.
}
}
Now you are able to go to your solution explorer, right click on your controllers folder and click add - Controller. Then specify that you want MVC 5 Controller with views, using Entity Framework. Click - Add.
A dialog will show, specify your view model, the context class we set up, make sure "Generate Views" is selected, name your controller and BAM! Build your project and view your auto created everything!
I'm trying to find the best correct way to do the following:
I have a ViewModel for a character editor called CharacterViewModel. This CharacterViewModel is populated with a Character object, a list of available ability scores a character can have, which are in another table.
I created an edit template for the drop down, and I'm trying to find a way to recuperate the list of edited abilities. I can't seem to get them back on the controller.
Here is the ViewModel code:
public class CharacterViewModel : DbContext
{
public Character Character { get; set; }
[UIHint("CharacterAbilityScores")]
public IEnumerable<CharacterAbilityScore> CharacterAbilityScores { get; set; }
public IEnumerable<SelectListItem> AbilityScoresSelectList { get; set; }
public IEnumerable<AbilityModifiersAndBonusSpellDTO> AbilityModifiersAndBonusSpellDTO { get; set; }
public CharacterViewModel()
: base("name=CharacterModels")
{
}
}
Here is the controller code for populating the ViewModel:
public async Task<ActionResult> Edit(int? id)
{
Character character = db.Characters.Find(id);
var model = new CharacterViewModel();
model.Character = character;
model.CharacterAbilityScores = character.CharacterAbilityScores;
// Creating the list of ability scores for the view
model.AbilityScoresSelectList = from amabs in db.AbilityModifiersAndBonusSpells
select new SelectListItem()
{
Value = amabs.score.ToString(),
Text = amabs.score.ToString()
};
return View(model);
}
The edit method signature in the controller (the CharacterAbilityScores property and the other complex ones are always empty on the return trip):
public async Task<ActionResult> Edit(CharacterViewModel characterViewModel)
Here is the related code in the edit view:
#model CampaignManager.Models.CharacterViewModel
#using (Html.BeginForm())
{
<div class="form-group">
#Html.EditorFor(model => model.CharacterAbilityScores, new { AbilityScoresSelectList = Model.AbilityScoresSelectList })
</div>
}
And finally, the EditorTemplate:
#model IEnumerable<CampaignManager.Entities.CharacterAbilityScore>
<table>
#foreach (var abilityScore in Model)
{
<tr>
<td>#abilityScore.Ability.Abbreviation</td>
<td>
#{
if (ViewData["AbilityScoresSelectList"] != null)
{
#Html.HiddenFor(z => abilityScore);
#Html.HiddenFor(z => abilityScore.AbilityId);
#Html.DropDownListFor(x => abilityScore.AbilityId, (IEnumerable<SelectListItem>)ViewData["AbilityScoresSelectList"], dropDownHTMLOptions);
}
}
</td>
<tr>
}
</table>
I've tried many different HiddenFor tricks, storing the whole collection, storing different id's... I'm a bit lost in there I'll admit. Maybe I'm doing this all wrong and I need another approach?
UPDATE
Here is the model for the CharacterAbilityScore entity:
public partial class CharacterAbilityScore
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CharacterId { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AbilityId { get; set; }
public int AbilityScore { get; set; }
public virtual Ability Ability { get; set; }
public virtual AbilityModifiersAndBonusSpell AbilityModifiersAndBonusSpell { get; set; }
public virtual Character Character { get; set; }
}
EditorFor() is designed to wok with collection where the EditorTemplate is the type in the collection (in your case you have made the EditorTemplate's model a collection (not the type) and are then giving each element a duplicate id attribute (invalid html) and duplicate name attributes (which cant be bound to a collection).
Change the template (Views/Shared/EditorTemplates/CharacterAbilityScore.cshtml) to:
#model CampaignManager.Entities.CharacterAbilityScore
<tr>
<td>#Html.DisplatFor(m => m.Ability.Abbreviation)</td>
<td>#Html.DropDownListFor(m => m.AbilityId, (IEnumerable<SelectListItem>)ViewData["AbilityScoresSelectList"])</td>
</tr>
and in the main view
#model CampaignManager.Models.CharacterViewModel
#using (Html.BeginForm())
{
<table>
#Html.EditorFor(model => model.CharacterAbilityScores, new { AbilityScoresSelectList = Model.AbilityScoresSelectList })
</table>
}
Side notes:
You have not posted the model for CharacterAbilityScore so a have
assumed it contains properties Abbreviation (for display only) and
AbilityId (associated with the dropdown).
You can not use #Html.HiddenFor() on a complex object (the value
will be the .ToString() output of the object) and having
#Html.HiddenFor() for the same property as the dropdown (and
located before #Html.DropDownListFor()) means that you will bind
to the hidden input on post back (i.e. the original value, not the
selected value from the dropdown)
I also recommend your view models do not derive from DbContext.
The purpose of a view model is to define the properties you want to
display/edit in the view
i am trying to get the value of selected check-box using model but not able to get as i want ;
Below is the table image
below is my code for this VIEW
And below is the code for result.I get null value
And below is my model declaration
public class RoleDetail
{
[Key]
public int RoleDetailID { get; set; }
public bool IsCreate { get; set; }
public bool IsDelete { get; set; }
public bool IsView { get; set; }
public bool IsEdit { get; set; }
public bool IsDownload { get; set; }
public string ControllerName { get; set; }
public System.DateTime CreateDate { get; set; }
public Int32 UserTypeId { get; set; }
}
public enum ControllerName
{
Account, Candidate, Career, ChooseUs, ContactUs, DocumentType, Employee, Gallery, GalleryType, GetTouch, Home, JobCategory, Jobs, Portfolio, ResumeUpload, RoleDetail, Services, User, UserRoleType
}
Replace the foreach loop in your view with a for:
#for (var i = 0; i < lst.Count; i++)
{
...
#Html.CheckBoxFor(x => lst[i].IsCreate)
#Html.CheckBoxFor(x => lst[i].IsView)
#Html.CheckBoxFor(x => lst[i].IsDelete)
...
}
For this to work make sure that the variable you are iterating over is an IList<T> or T[].
Also your controller action argument should be named accordingly:
public ActionResult Create(IEnumerable<RoleDetail> lst)
{
...
}
You should not be creating RoleDetail in the view. In the GET method create a List<RoleDetail>, populate it with the objects you want to display and return it to the view.
Controller
public ActionResult Create()
{
List<RoleDetail> model = new List<RoleDetail>();
// populate the collection, for example
foreach(var name in Enum.GetNames(typeof(ControllerName)))
{
model.Add(new RoleDetail()
{
ControllerName = name,
IsCreate = true // etc
});
}
return View(model);
}
public ActionResult Create(IEnumerable<RoleDetail> model)
{
}
View
#model List<RoleDetail>
#using(Html.BeginForm())
{
for(int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m.ControllerName) // needed for postback
#Html.DisplayFor( => m.ControllerName)
#Html.CheckBoxFor(m => m.IsCreate)
....
}
<input type="submit" />
}
Side notes
Do not try to override the name (or value) attribute. The html
helper set these correctly for model binding (and in any case you
were only setting it to the value the helper generated anyway)
The reason the foreach loop does not work is your generating
duplicate name attributes (and also invalid html due to duplcate
id attributes). The for loop correctly generates the correct
names with indexers (e.g. <input name="[0].IsCreate " ..>, <input
name="[1].IsCreate " ..> etc.
You don't appear to be rendering controls for all of you model
properties so use a view model containing only those properties you
need to display/edit. What is a view model in MVC
You have public enum ControllerName so I suspect property
ControllerName in RoleDetail should be public ControllerName ControllerName { get; set; }?
And in future, post the code, not an image of it!