I'm having hard time trying to figure out which way to go.
Before i start, here some of the classes :
public class Car
{
[Key]
[Required]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[MaxLength(255)]
public string Title { get; set; }
[Required]
public DateTime Date{ get; set; }
public virtual Category Category { get; set; }
}
public class Category {
[Key]
[Required]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ID {get;set;}
public Category Parent{get;set;}
[Required]
[MaxLength(255)]
public string Title {get;set;}
}
So, to create a new "Car", I simply need a page with a dropdown for all of the categories. To do that, I wrote a new class and passed it to "ActionResult Create"...
Here it is :
public class CarCreate
{
public Car Car { get; set; }
public List<Category> Categories
{
get
{
List<Category> list = new List<Category>();
SystemContext sc = new SystemContext();
list = sc.Categories.ToList();
list.Insert(0, null);
sc.Dispose();
return list;
}
}
}
And from the controller I passed it like this:
public ActionResult Create()
{
return View(new CarCreate());
}
And within the view, I created a dropdown for List in CarCreate class:
<div class="col-md-4">
#Html.LabelFor(t => t.Car.Title)
#Html.TextBoxFor(model => model.Car.Title, new { #class = "form-control" })
</div>
<div class="col-md-4">
#Html.LabelFor(t => t.Car.Category)
#Html.DropDownListFor(model => model.Car.Category, new SelectList(Model.Categories, "ID", "Title","Select"), new { #class = "form-control" })
</div>
And when I check the Car.Category within the ActionResult that handles postback like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CarCreate emc)
{
Response.Write(emc.Car.Title);
Response.Write(emc.Car.Category== null ? "null" : "notnull");
return View(new CarCreate());
}
I can get title. But category is always null.
What would I have to do to get category from postback?
DropDownListFor is mapped with a value wich is defined by the option selected in the select.
You try to set this value in a complex object.
I think you should have a things like
public class Car
{
[Key]
[Required]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[MaxLength(255)]
public string Title { get; set; }
[Required]
public DateTime Date{ get; set; }
public virtual int CategoryID { get ; set;}
public virtual Category Category { get; set; }
}
And
<div class="col-md-4">
#Html.LabelFor(t => t.Car.Category)
#Html.DropDownListFor(model => model.Car.CategoryID, new SelectList(Model.Categories, "ID", "Title","Select"), new { #class = "form-control" })
</div>
I believe it's because your Category is not instanciate. Try to add this in the Car constructor :
public class Car
{
public Car()
{
this.Category = new Category();
}
}
Hope it helps !
I would keep my viewmodels as lite as i can. Just keep the properties you really need in your view. Also i prefer to keep those as simple POCO's(not with data loading logic in that) so that i can use that in many places.
public class CreateCarVM
{
[Required]
public string Title { set;get;}
public List<SelectListItem> Categories { set;get;}
[Required]
public int SelectedCategory { set;get;}
public CreateCarVM()
{
Categories =new List<SelectListItem>();
}
}
and in my create action method,
public ActionResult Create()
{
var vm=new CreateCarVM();
vm.Categories=GetCategories();
return View(vm);
}
private List<SelectListItem> GetCategories()
{
var list=new List<SelectListItem>();
//2 items hard coded for demo. You may load it from db
list.Add(new SelectListItem { Value="1", Text="Sedan"});
list.Add(new SelectListItem { Value="2", Text="SUV"});
return list;
}
and in your View which is strongly typed to the CreateCarVM
#model CreateCarVM
#using(Html.BeginForm())
{
#Html.TextBoxFor(s=>s.Title)
#Html.DropDownListFor(s=>s.SelectedCategory,Model.Categories,"Select")
<input type="submit" />
}
Now when the form is posted, You can check the SelectedCategory property of the viewmodel
[HttpPost]
public ActionResult Create(CreateCarVM model)
{
if(ModelState.IsValid)
{
//check for model.SelectedCategory now
// to do :Save and Redirect (PRG pattern)
}
model.Categories=GetCategories();
return View(model);
}
Related
My input model consists of NewForm in which many fields
public class NewForm
{
[Key]
public int Id { get; set; }
public string HeadForm { get; set; }
public List<Field> Fields { get; set; }
}
public class Field
{
public int Id { get; set; }
public bool Check { get; set; }
public string HeadField { get; set; }
}
I want to take values from the base and edit them, but Model.Fields.Count throw exception. Although the string "HeadForm" is displayed. Lists are not displayed.
EditPage:
#model EditFormApplication.Models.NewForm
#using (Html.BeginForm("Edit", "Home", FormMethod.Post))
{
#Html.TextBoxFor(model => model.HeadForm)
<h5>Fields:</h5><br>
#for ( var i = 0; i< Model.Fields.Count; i++) {
#Html.TextBoxFor(model => model.Fields[i].HeadField)
#Html.CheckBoxFor(model => model.Fields[i].Check)
}
<input type="button" value="Add Field" onclick="addField(this);">
<input type="submit"value="Save">
}
For example, I am typing data by ID = 3.
Controller:
public ActionResult CreateForm(NewForm model)
{
using (NewFormContext db = new NewFormContext())
{
db.NewForms.Add(model);
db.SaveChanges();
return RedirectToAction("Edit");
}
}
public ActionResult Edit()
{
using (NewFormContext db = new NewFormContext()) {
var model = db.NewForms.Find(3);
return this.View(model);
}
}
used Code First and one to many
Sounds like Model.Fields property still contain null when db.NewForms.Find() is executed to assign the model you want to return into view, indicating that EF doesn't create dependent collection yet. As far as I know you should add collection instance definition inside parameterless constructor of entity class:
public class NewForm
{
public NewForm()
{
// instantiate list here
Fields = new List<Field>();
}
[Key]
public int Id { get; set; }
public string HeadForm { get; set; }
public List<Field> Fields { get; set; }
}
Or if you're using lazy loading, mark the property as virtual to let EF instantiate the collection while necessary:
public virtual List<Field> Fields { get; set; }
Related issue:
EF codefirst : Should I initialize navigation properties?
I'm using mvc4. How can I Bind CuntryName and its values in DropdownList
Country?
public class Country
{
public int Cnt_Id { get; set; }
public string Cnt_Name { get; set; }
}
This is my private class
public class HybridEmployee
{
public IEnumerable<Country> GetCount { get; set; }
}
Controller
public ActionResult GetCountry()
{
var x = ObjRepo.GetCountry();
hybrid.GetCount = x;
return View(hybrid);
}
Index.cshtml
#model Mvc_Application.Models.HybridEmployee
#using Mvc_Application.Models
#using (Html.BeginForm("SaveEmp", "Home", FormMethod.Post))
{
#Html.DropDownListFor(x=>x.GetCount.FirstOrDefault().Cnt_Id),new SelectList(Model.GetCount,"","");
}
We can have two approaches as shown below:
Using a ViewBag containing the data for dropdown list.
Model file:
public class State
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int CountryID { get; set; }
}
In .cs file:
ViewBag.Countries = countryService.All().Select(x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList();
In .cshtml file:
#Html.DropDownListFor(x => x.CountryID, ViewBag.Countries as IEnumerable<SelectListItem>, "Select Country", new { #class = "form-control" })
Using a Model's property containing the data for dropdown list.
Model file:
public class State
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int CountryID { get; set; }
public List<SelectListItem> Countries { get; set; }
}
In .cs file:
model.Countries = countryService.All().Select(x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList();
In .cshtml file:
#Html.DropDownListFor(x => x.CountryID, Model.Countries, "Select Country", new { #class = "form-control" })
This expression totally doesn't makes sense:
#Html.DropDownListFor(x=>x.GetCount.FirstOrDefault().Cnt_Id),new SelectList(Model.GetCount,"","");
The first argument of DropDownListFor helper (Expression<Func<TModel, TProperty>> expression) doesn't use LINQ expression, it is model binding expression - you must use a property to hold selected value instead. The drop down list binding should be used like this:
Model
public class Country
{
public int Cnt_Id { get; set; }
public string Cnt_Name { get; set; }
public int SelectedCountry { get; set; } // additional property to get selected value
}
View
#Html.DropDownListFor(x => x.SelectedCountry, new SelectList(Model.GetCount, "Cnt_Id", "Cnt_Value"), null)
There is 1 form on which i will ask for Academic Details for :
Graduation
Post Graduation(Masters)
Professional Qualification.
So far any user say UserId="1" 3 entries will be created in my AcademicMaster each for bachelor,Master(Post Graduation) and Professional Qualification.
My Database AcademicMaster table fields and datamodel:
Id,Qualification(GraduationCourses),Acheievement,UserId
View Model:
public class AcademicViewModel
{
public int Id { get; set; }
public virtual Graduation Graduation{ get; set; }
public virtual PostGraduation PostGraduation{ get; set; }
public virtual ProfessionalQualification ProfessionalQualification{ get; set; }
}
public class Graduation
{
public string BachelorQualification { get; set; }
public string BachelorAchievement { get; set; }
}
public class PostGraduation
{
public string MasterQualification { get; set; }
public string MasterAchievement { get; set; }
}
public class ProfessionalQualification
{
public string ProfessionalQualifications { get; set; }
}
So my View is like this:
#model AcademicViewModel
#{
IEnumerable<SelectListItem> graduationList = ViewBag.GraduationList;
IEnumerable<SelectListItem> postGraduationList = ViewBag.PostGraduationList;
}
#using (Html.BeginForm())
{
<div class="row">
Bachelors
#Html.DropDownListFor(model => model.Graduation.Qualification, graduationList)
</div>
#Html.TextAreaFor(model => model.Graduation.Achievement)
<div class="row">
MASTERS
#Html.DropDownListFor(model => model.PostGraduation.Qualification, postGraduationList)
</div>
#Html.TextAreaFor(model => model.PostGraduation.Achievement)
<div class="row">
PROFESSIONAL QUALIFITCATION
#Html.TextAreaFor(model => model.ProfessionalQualification.ProfessionalQualifications)
</div>
<input type="submit" value="Save">
}
This is my Controller:
[HttpPost]
public ActionResult MyController(AcademicViewModel model)
{
//Actions
}
So is my View Model structure appropriate and how to create 3 entries in AcademicMaster Table??
I will start by saying that having one table may not be the best choice (what happens if later you start adding additional properties which may be applicable to Graduation that are not applicable to Professional - for example YearOfGraduation - you could end up with a huge number of fields, many of which may have null values.
However, if you want one table, then at least add another field so that you can identify if the data is related to Graduation, PostGraduation or Professional. The associated data model for the AcademicMasters table would be
public class AcademicMaster
{
public int ID { get; set; }
public string Type { get; set; } // may be an enum?
public string Qualification { get; set; }
public string Achievement { get; set; }
public int UserID { get; set; }
}
Side note: It might be better to use an enum for the Type property
public enum AcademicType
{
Graduation,
PostGraduation,
Professional
}
There does not seem to be any need to your current Graduation, PostGraduation and ProfessionalQualification models and your view model should be
public class AcademicViewModel
{
public string GraduationQualification { get; set; }
public string GraduationAchievement { get; set; }
public string PostGraduationQualification { get; set; }
public string PostGraduationAchievement { get; set; }
public string ProfessionalAchievement { get; set; }
public IEnumerable<SelectListItem> GraduationList { get; set; }
public IEnumerable<SelectListItem> PostGraduationList { get; set; }
}
Side notes: Its not clear what your current ProfessionalQualifications property is - does that get assigned to the Qualification field or the Acheievement field in the database? Since your using a view model, then it should include the SelectList's rather that using ViewBag.
Then your view will be
#model AcademicViewModel
#using (Html.BeginForm())
{
<h2>Graduation</h2>
#Html.DropDownListFor(m => m.GraduationQualification, Model.GraduationList)
#Html.TextAreaFor(m => m.GraduationAchievement)
... // repeat for PostGraduation and Professional
<input type="submit" value="Save">
}
And the POST method would be
[HttpPost]
public ActionResult MyController(AcademicViewModel model) // should be named Create?
{
var userID = ....
AcademicMaster graduation = new AcademicMaster
{
Type = AcademicType.Graduation,
Qualification = model.GraduationAchievement,
Achievement = model.GraduationAchievement,
UserId = userID;
};
db.AcademicMasters.Add(graduation);
// Repeat for PostGraduation and Professional
db.SaveChanges();
// redirect?
}
Last day, i saw that in my razor view
#Html.DropDownListFor(x => x.FoodId,null, htmlAttributes: new { #class = "form-control" })
// and (just another alternative)
#Html.DropDownList("FoodId", null, htmlAttributes: new { #class = "form-control" })
And i'm wondering what's going on with the data in the list, where and when data-filling occurs? and what is the difference between the two lines? and how the dropdown got filled with data (because there is data in the execution of the code)
sorry for my bad english
Update #1
Model:
Food.cs and User.cs
public partial class Food
{
public Food()
{
this.User = new HashSet<User>();
}
public System.Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> User { get; set; }
}
//
public partial class User
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<System.Guid> FoodId { get; set; }
public virtual Food Food { get; set; }
}
Controller (only action)
public ActionResult Create()
{
ViewBag.FoodId = new SelectList(db.FoodSet, "Id", "Name");
return View();
}
Omg thanks i'm now stupid :)
Answer: Got filled with ViewBag.FoodId
When passing a null value to the second argument, it it will automatically, even without javascript and with a cleared cache look in the ViewBag for a key with the same name as the dropdown, otherwise launch exception.
In my case, null was passed and it checked the ViewBag and found a key
On the register page I have added a dropdown for vdab student: Yes / no But the validation described in the viewmodel is not working (I guess this is because I used ViewBag and not model?)
Can someone please help me to create the dropdown so I Can use this way:
#Html.DropDownFor(x => x.Something, Model.Something, "Select a component type" )
This is what I have:
IdentityModel
public class ApplicationUser : IdentityUser
{
public virtual Person Person { get; set; }
}
public class Person
{
public virtual Klas Klas { get; set; }
public int Id { get; set; }
public string Voornaam { get; set; }
public string Familienaam { get; set; }
public bool Vdab { get; set; }
}
public class Klas
{
public int Id { get; set; }
public string Omschrijving { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("ConnectionString")
{
}
}
RegisterViewModel:
public class RegisterViewModel
{
[Required]
[Display(Name = "Gebruikersnaam")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Het {0} moet misntens {2} Karakters lang zijn.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Wachtwoord")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Bevestig Wachtwoord")]
[Compare("Password", ErrorMessage = "De wachtwoorden komen niet overeen")]
public string ConfirmPassword { get; set; }
[Required]
[Display(Name = "Voornaam")]
public string Voornaam { get; set; }
[Required]
[Display(Name = "Familienaam")]
public string Familienaam { get; set; }
[Required]
[Display(Name = "Klas")]
public string Klas { get; set; }
[Required]
[Display(Name = "Vdab Student")]
public bool Vdab { get; set; }
}
And in the AcountController:
[AllowAnonymous]
public ActionResult Register()
{
var Vdab = new SelectList(new[] { new { ID = "1", Name = "Ja" }, new { ID = "0", Name = "Nee" }, }, "ID", "Name", 1);
ViewBag.Vdab = Vdab;
return View();
}
And finally I show the dropdown in the view with razor
<div class="form-group">
#Html.LabelFor(m => m.Vdab, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("Vdab", #ViewBag.Vdab as SelectList, "----Maak een keuze ----")
</div>
</div>
You can use:
#Html.DropDownList("Vdab", #ViewBag.Vdab as SelectList, "----Maak een keuze ----")
Just in your controller you could do like this:
[AllowAnonymous]
public ActionResult Register()
{
List<SelectListItem> myList =new List<SelectListItem>();
myList.Add(new SelectListItem { Value = bool.TrueString, Text = "Ja" });
myList.Add(new SelectListItem { Value = bool.FalseString, Text = "Nee" });
var Vdab = new SelectList(myList,"Value","Text");
ViewBag.Vdab = Vdab;
return View();
}
Add this to your RegisterViewModelClass:
public IEnumerable<SelectListItem> VdabSelectList { get; set; }
And in the controller:
[AllowAnonymous]
public ActionResult Register()
{
RegisterViewModel viewModel = new RegisterViewModel();
viewModel.VdabSelectList = new[]
{
new SelectListItem { Value = bool.TrueString, Text = "Ja" },
new SelectListItem { Value = bool.FalseString, Text = "Nee" }
}
return View(viewModel);
}
[HttpPost]
public ActionResult Register(RegisterViewModel _viewModel)
{
//Do stuff with form data...
}
Finally, add this to your view:
<div class="form-group">
#Html.LabelFor(m => m.Vdab, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.vDab, new SelectList(m.VdabSelectList, "Value", "Text"))
</div>
</div>
Disclaimer: I haven't tried a DropDownListFor with a boolean, but based on this post, it looks like it should work. I always use radio buttons for booleans, like this:
#Html.RadioButtonFor(m => m.Vdab, true)Ja
#Html.RadioButtonFor(m => m.Vdab, false)Nee
Your problem is simply that you have specified that your model type is bool. Bool is a value type, and is always going to contain a value, thus validation can never fail, no matter what. It always contains at least the default value, which is false.
You want to make the type nullable.
[Required]
[Display(Name = "Vdab Student")]
public bool? Vdab { get; set; }
Now, your required validation will work correctly, and your original (and correct) Html helper will work.
#Html.DropDownFor(x => x.Something, Model.SomethingList, "Select a component type" )
Word of warning, though.. Change your list to Model.SomethigList, using the same name for your Selected item and list can cause a lot of problems.