i want to create users with special features in mvc. when user is going to create i want to assign some special feature to each user like particular user having his own house, having his own car using checkbox selection. the particular feature is reside in different table named feature. then how can i add those features with user while creating the user.
i have created a view model named ViewModelUserWithFeature
public class ViewModelUserWithFeature
{
public User User { get; set; }
public Feature Feature { get; set; }
public List<Feature> feature { get; set; }
public IEnumerable<User> IUser { get; set; }
private UserDbContext userDbContext;
private IUserService userService;
public void ViewUserList()
{
userService = new RoleService(userDbContext);
IUser = userService.GetUsers();
}
public void AddNewUser(User userAdd)
{
userService = new UserService(userDbContext);
User = userService.AddUser(userAdd);
userService.SaveUser();
}
}
here is my view in which i want to two textboxes and a list of features which are going to select by checkbox and attached with the user.
#model App.ViewModel.ViewModelUserWithFeature
#using (Html.BeginForm("Create", "User", FormMethod.Post))
{
<div>
#Html.TextBoxFor(m => m.User.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m => m.UserAddres)
</div>
#for(int i=0; i < Model.Feature; i++)
{
<div class="cb"><input type="checkbox" name="checkbox"></div>
<div class="per-content">
<label for="1"> Model.Feature.FeatureName</div>
}
<div>
<button type="submit" id="btn-rd">Submit</button>
</div>
}
Controller
[HttpPost]
public ActionResult Create(User user)
{
ViewModelUserWithFeature viewModelUserWithFeature = new ViewModelUserWithFeature(usertDbContext);
if (ModelState.IsValid)
{
viewModelUserWithFeature.AddNewUser(user);
}
return RedirectToAction("Index", viewModelUserWithFeature);
}
not able to achieve that what i have tried so far i have mentioned . please help. thanks in advance.
Use view models to represent what you display and edit
public class FeatureVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class UserVM
{
public string Name { get; set; }
public string Address { get; set; }
public List<FeatureVM> Features { get; set; }
}
Controller
public ActionResult Create()
{
UserVM model = new UserVM();
model.Features = // map all available features
return View(model);
}
[HttpPost]
public ActionResult Create(UserVM model)
{
}
View
#model UserVM
#using(Html.BeginForm())
{
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
.....
for(int i = 0; i < Model.Features.Count; i++)
{
#Html.HiddenFor(m => m.Features[i].ID)
#Html.CheckBoxFor(m => m.Features[i].IsSelected)
#Html.LabelFor(m => m.Features[i].IsSelected, Model.Features[i].Name)
}
<input type="submit" value="Create" />
}
try with this, in you Model of Feature add a new property
public bool isFeatureOf { get; set; }
also in your model for the method AddNewUser change it to
public void AddNewUser(User userAdd,List<Feature> features)
{
userService = new UserService(userDbContext);
User = userService.AddUser(userAdd);
userService.SaveUser();
//featureService = new FeatureService(yourdbcontext)
foreach (Feature item in features)
{
//save to db
featureService.SaveFeature(item,User.Id);
//i don't know if in your database you already have a table,colum or something to map the features by user
}
}
then in your view
for(int index=0; index < Model.Features.Count(); index++)
{
#Html.HiddenFor(m=>Model.Features[index].NameFeature)
#Html.Raw(Model.Features[index].NameFeature)
#Html.CheckBoxFor(m=>Model.Features[index].isFeatureOf)
}
also in your view you'll need to change this
<div>
#Html.TextBoxFor(m => m.User.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m => m.UserAddres)
</div>
to:
<div>
#Html.TextBoxFor(m =>Model.User.UserName)
#Html.ValidationMessageFor(m => Model.User.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m =>Model.User.UserAddres)
</div>
in your controller change your param to get the whole Model like this
[HttpPost]
public ActionResult Create(ViewModelUserWithFeature model)
{
if (ModelState.IsValid)
{
model.AddNewUser(model.User,model.Features);
}
return RedirectToAction("Index", viewModelUserWithFeature);
}
hope this can help you
Related
I have a model view that has a list of objects in it, so it has a list of other class in it:
public class CpdTestFeedbackFormModel
{
public Guid WebsiteKey { get; set; }
//
//some other simple fileds
public List<SelectListItem> StarItems;
public List<CpdFeedbackQuestionAnswerModel> Questions;
public CpdTestFeedbackFormModel()
{
//some initialization
}
}
and the other class definition:
public class CpdFeedbackQuestionAnswerModel
{
public Guid Questionkey { get; set; }
public string Question { get; set; }
public string QuestionType { get; set; }
public int RatingAnswer { get; set; }
public string TextAnswer { get; set; }
}
I render my data like this in the view:
using (Html.BeginForm("SubmitFeedbackAnswers", "Form", FormMethod.Post, new
{
enctype = "multipart/form-data"
}))
{
#Html.HiddenFor(x => Model.WebsiteKey)
#Html.HiddenFor(x => Model.Testkey)
<div>
<div>
<h1>Feedback Questions</h1>
#for (int i = 0; i < #Model.Questions.Count(); i++)
{
<p>Q.<span>#Model.Questions[i].Question</span></p>
<p>
#if (#Model.Questions[i].QuestionType == "Star Rating")
{
#foreach (var starItem in Model.StarItems)
{
#Html.RadioButtonFor(model => Model.Questions[i].RatingAnswer, starItem.Value)
#Html.Label(starItem.Text)
}
}
else
{
#Html.HiddenFor(m => Model.Questions[i].Questionkey)
#Html.HiddenFor(m => Model.Questions[i].Question)
#Html.HiddenFor(m => Model.Questions[i].QuestionType)
#Html.HiddenFor(m => Model.Questions[i].RatingAnswer)
#Html.TextBoxFor(m => Model.Questions[i].TextAnswer)
}
</p>
}
</div>
</div>
<div>
<button type="submit" data-type="submit" class="bg-btn-primary">
Save Answer
</button>
</div>
</div>
}
when I inspect the view binding seems perfect:
but in my controller, the questions field is empty all other data are coming ok except that list.
[HttpPost]
public async Task<StatusCodeResult> SubmitFeedbackAnswers(CpdTestFeedbackFormModel model)
did I miss something?
After having another look I noticed a mistake in my CpdTestFeedbackFormModel class, my Questions don't have a get; set; method!! and that's it, it's working fine now.
public List<CpdFeedbackQuestionAnswerModel> Questions { get; set; }
Only missed part.
This is my Controller code.
public ActionResult Create()
{
ViewBag.grp_id = new SelectList(db.tm_grp_group, "grp_id", "grp_name");
ViewBag.perm_id = new SelectList(db.tm_perm_level, "perm_id", "perm_levelname");
return View();
}
Below is my view code.
#model Permission.ts_grp_perm_mapping
....
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ts_grp_perm_mapping</legend>
<div class="editor-label">
#Html.LabelFor(model => model.grp_permid)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.grp_permid)
#Html.ValidationMessageFor(model => model.grp_permid)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.grp_id, "tm_grp_group")
</div>
<div class="editor-field">
#Html.DropDownList("grp_id", String.Empty)
#Html.ValidationMessageFor(model => model.grp_id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.perm_id, "tm_perm_level")
</div>
<div class="editor-field">
#Html.DropDownList("perm_id", String.Empty)
#Html.ValidationMessageFor(model => model.perm_id)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
In controller ViewBag.perm_id contains some values (this is foreign key). In view perm.id displays in the form of dropdownbox but I want it in the form of checkboxlist. How can I achieve this?
This is the viewmodel I created.
public class AssignUserViewModel
{
public tm_perm_level[] perms { get; set; }
public int grp_id { get; set; }
}
Now in controller how can i send values to view? This is my tm_perm_level model
public partial class tm_perm_level
{
public tm_perm_level()
{
this.ts_grp_perm_mapping = new HashSet<ts_grp_perm_mapping>();
}
public int perm_id { get; set; }
public string perm_levelname { get; set; }
public string perm_description { get; set; }
public bool perm_status { get; set; }
public virtual ICollection<ts_grp_perm_mapping> ts_grp_perm_mapping { get; set; }
}
This is ts_grp_perm_mapping model
public partial class ts_grp_perm_mapping
{
public ts_grp_perm_mapping()
{
this.ts_perm_levelmapping = new HashSet<ts_perm_levelmapping>();
}
public int grp_permid { get; set; }
public int grp_id { get; set; }
public int perm_id { get; set; }
public List<tm_perm_level> permissions { get; set; }
public virtual tm_grp_group tm_grp_group { get; set; }
public virtual tm_perm_level tm_perm_level { get; set; }
public virtual ICollection<ts_perm_levelmapping> ts_perm_levelmapping { get; set; }
}
Especially when editing, always start with view models to represent what you want to display (refer What is ViewModel in MVC?)
public class PermissionVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class GroupPermissionVM
{
public int GroupID { get; set; }
public IEnumerable<SelectListItem> GroupList { get; set; }
public IEnumerable<PermissionVM> Permissions { get; set; }
}
Then create an EditorTemplate for PermissionVM. In the /Views/Shared/EditorTemplates/PermissionVM.cshtml folder
#model PermissionVM
<div>
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name)
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Name)
</div>
and the main view will be
#model GroupPermissionVM
....
#using (Html.BeginForm())
{
// dropdownlist
#Html.LabelFor(m => m.GroupID)
#Html.DropDownListFor(m => m.GroupID, Model.GroupList, "Please select")
#Html.ValidationMessageFor(m => m.GroupID)
// checkboxlist
#Html.EditorFor(m => m.Permissions)
<input type="submit" value="Create" />
}
The controller methods would then be
public ActionResult Create()
{
var groups = db.tm_grp_group;
var permissions = db.tm_perm_level;
GroupPermissionVM model = new GroupPermissionVM
{
GroupList = new SelectList(groups, "grp_id", "grp_name"),
Permissions = permissions.Select(p => new PermissionVM
{
ID = p.perm_id,
Name = p.perm_levelname
}
};
return View(model);
}
[HttpPost]
public ActionResult Create(GroupPermissionVM model)
{
if (!ModelState.IsValid)
{
var groups = db.tm_grp_group;
model.GroupList = new SelectList(groups, "grp_id", "grp_name");
return View(model);
}
// map the view model to a new instance of your data model(s)
// note: to get the ID's of the selected permissions -
// var selectedPermissions = model.Permissions.Where(p => p.IsSelected).Select(p => p.ID);
// save and redirect
}
Side note: I strongly recommend you follow normal naming conventions
Edit
Based on OP's comment for an option using radio buttons to select only one item, the revised code would be
public class PermissionVM
{
public int ID { get; set; }
public string Name { get; set; }
}
public class GroupPermissionVM
{
public int GroupID { get; set; }
public int PermissionID { get; set; }
public IEnumerable<SelectListItem> GroupList { get; set; }
public IEnumerable<PermissionVM> Permissions { get; set; }
}
and the view would be (no separate EditorTemplate required)
#model GroupPermissionVM
....
#using (Html.BeginForm())
{
// dropdownlist as above
// radio buttons
foreach (var permission in Model.Permissions)
{
<label>
#Html.RadioButtonForm(m => m.PermissionID, permission.ID)
<span>#permission.Name</span>
</label>
}
<input type="submit" value="Create" />
}
and in the POST method, the value of model.PermissionID will contain the ID of the selected Permission.
I have 2 ViewModels - User and Reminder.
public class UserViewModel
{
[Required]
[Display(Name = "Your name")]
public string Name { get; set; }
[Display(Name = "Your reminders")]
public IEnumerable<ReminderViewModel> Reminders { get; set; }
}
public class ReminderViewModel
{
[Required]
[Display(Name = "Time")]
public TimeSpan Time { get; set; }
[Required]
[Display(Name = "Frequency of repair")]
public string Frequency { get; set; }
}
My add-view:
#using (Html.BeginForm("Add", "Test"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#ViewBag.Status
<ul style="list-style: none;">
<li>
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</li>
???????????? // Reminders
<li>
<input type="submit" value="Add" /></li>
</ul>
}
My question is: how can I bind this IEnumerable<ReminderViewModel> in my View?
I want to achieve situation, where user can put some reminders (selecting Time and Frequency) many times, before send click.
How can I do this?
You need to do the following:
First add a reference to the model in the top of the view like this:
#model UserViewModel
Including your namespace.
Change your collection to user a list so it can be indexed and model binded like this:
#for (var i = 0; i < Model.Reminders.Count(); i++)
{
#Html.LabelFor(m => Model.Reminders[i].Frequency)
#Html.TextBoxFor(m => Model.Reminders[i].Frequency)
#Html.ValidationMessageFor(m => Model.Reminders[i].Frequency)
#Html.LabelFor(m => Model.Reminders[i].Time)
#Html.TextBoxFor(m => Model.Reminders[i].Time)
#Html.ValidationMessageFor(m => Model.Reminders[i].Time)
}
This in your model:
[Display(Name = "Your reminders")]
public List<ReminderViewModel> Reminders { get; set; }
You also need to change your Begin form to set it to post like this:
#using (Html.BeginForm("Add", "Test", FormMethod.Post))
Finally you need to implement the post like this taking the model as a parameter:
[HttpPost]
public ActionResult Add(UserViewModel model)
{
// Do processing here
return View(model);
}
First, you need to change it to List<ReminderViewModel>. In order to get proper model binding you must be able to select the objects using an index value.
Then in your view:
#for (var i = 0; i < Model.Reminders.Count(); i++)
{
#Html.LabelFor(m => m.Reminders[i].Time)
#Html.TextBoxFor(m => m.Reminders[i].Time)
#Html.ValidationMessageFor(m => m.Reminders[i].Time)
#Html.LabelFor(m => m.Reminders[i].Frequency)
#Html.TextBoxFor(m => m.Reminders[i].Frequency)
#Html.ValidationMessageFor(m => m.Reminders[i].Frequency)
}
If Reminders is initially null, you'll need to initialize it with one or more ReminderViewModels in your action:
var model = new UserViewModel {
Reminders = new List<ReminderViewModel> {
new ReminderViewModel()
}
}
return View(model);
Or you can do this in your UserViewModel's constructor:
public class UserViewModel
{
public UserViewModel()
{
Reminders = new List<ReminderViewModel> {
new ReminderViewModel();
}
}
}
I have an MVC model with a property that contains a generic collection of types that inherit from a single type. It displays the editor exactly as I would expect, but when I post back the types of all the items are the base type. How do I get it to return the correct types?
Model...
public class PageEM {
public long Id { get; set; }
public virtual IList<FieldEM> Fields { get; set; }
}
public class FieldEM { // I'd really like this to be abstract.
public long Id { get; set; }
public string Caption { get; set; }
public string Value { get; set; }
}
public class TextFieldEM : FieldEM {
}
public class CheckBoxFieldEM : FieldEM {
public bool ValueData {
get { return (bool)Value; }
set { Value = (string)value; }
}
PageEM View...
#model PageEM
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Fields</legend>
#Html.HiddenFor(m => m.Id)
#Html.EditorFor(m => m.Fields)
<input type="submit" value="Submit" title="Submit" />
</fieldset>
}
TextFieldEM Editor...
#model TextFieldEM
<div>
#Html.HiddenForFor(m => m.Id)
<div>
#Html.LabelFor(m => m.Value, Model.Caption)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Value)
#Html.ValidationMessageFor(m => m.Value)
</div>
</div>
CheckBoxFieldEM Editor...
#model CheckBoxFieldEM
<div>
#Html.HiddenForFor(m => m.Id)
<div class="editor-field">
#Html.EditorFor(m => m.DataValue)#Html.LabelFor(m => m.DataValue, Model.Caption, new { #class = "checkbox" })
</div>
</div>
Controller...
public partial class PageController : Controller {
public virtual ActionResult Edit() {
PageEM em = new PageEM() {
Id = 123,
Fields = new List<FieldEM>() {
new TextFieldEM() { Id = 1, Caption = "Text Line", Value = "This is test" },
new CheckBoxEM() { Id = 2, Caption = "Check here", ValueData = true }
}
};
return View(em);
}
[HttpPost]
public virtual ActionResult Edit(PageEM em) {
if (!ModelState.IsValid)
return View(em);
// but all of the em.Fields are FieldEM.
}
}
So how do I get it to post back with the subclassed FieldEMs?
You can't do that with the DefaultModelBinder. You'll have to create your own custom model binder in order to do what you want to do.
These might be helpful:
https://gist.github.com/joelpurra/2415633
ASP.NET MVC3 bind to subclass
ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism
I am still struggling with learning ASP.NET MVC. All my form entries are required so I would like to do validation on them. For brevity I have paired my model down to Description (textbox) and Paradigm (dropdown). I am including Entry.cs, Paradigm.cs and EntryViewModel.cs Model classes and the Display.cshtml View.
[Bind(Exclude = "EntryId")]
public class Entry
{
[ScaffoldColumn(false)]
public int EntryId { get; set; }
[Required(ErrorMessage = "You must include a description.")]
public string Description { get; set; }
[Display(Name = "Type")]
[Required(ErrorMessage = "You must select a type.")]
public int ParadigmId { get; set; }
public virtual Paradigm Paradigm { get; set; }
}
public class Paradigm
{
[ScaffoldColumn(false)]
public int ParadigmId { get; set; }
[Required]
public string Name { get; set; }
public List<Entry> Entries { get; set; }
}
public class EntryViewModel
{
public Entry Entry { get; set; }
public IEnumerable<Entry> Entries { get; set; }
}
#model Pylon.Models.EntryViewModel
#{
ViewBag.Title = "Display";
}
<hr />
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Entry</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Entry.Description)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Entry.Description)
#Html.ValidationMessageFor(model => model.Entry.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Entry.ParadigmId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Entry.ParadigmId, ((IEnumerable<Pylon.Models.Paradigm>)ViewBag.PossibleParadigms).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.ParadigmId.ToString(),
Selected = (Model != null) && (option.ParadigmId == Model.Entry.ParadigmId)
}))
<img src="../../Content/Images/add_icon.gif" />
#Html.ValidationMessageFor(model => model.Entry.ParadigmId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
If I submit the form without entering a description I would like validation to kick in and say "You must include a description." However instead I receive an ArgumentNullException on the DropDownFor line. http://www.wvha.org/temp/ArgumentNullException.png
What should I be doing? As an aside any decent books that cover ASP.NET MVC 3/Razor. I can follow along the basic tuts, but I go astray when I need to deviate to more advance features.
public class EntriesController : Controller
{
private readonly PylonContext _context = new PylonContext();
public ActionResult Display()
{
// DropDown
ViewBag.PossibleParadigms = _context.Paradigms;
var viewModel = new EntryViewModel {Entries = _context.Entries.ToList()};
return View(viewModel);
}
[HttpPost]
public ActionResult Display(EntryViewModel viewModel)
{
if (ModelState.IsValid)
{
_context.Entries.Add(viewModel.Entry);
_context.SaveChanges();
return RedirectToAction("Display");
}
return View(viewModel);
}
}
It's quite difficult to say without seeing your controller code, but looks like your ViewBag.PossibleParadigms might be null.
Does your insert/update controller action look something like this?
if (ModelState.IsValid) {
///...
} else {
return View(model);
}
If so, you need to put the PossibleParadigms back into the ViewBag (so to speak) before you return back to the view.
If you can post the relevant controller action code, it would be easier to know for sure.