ASP.Net MVC: How to bind dropdown with deep nested property - asp.net-mvc-4

suppose below is my view model classes.
public class MainViewModel
{
public List<Student> Students { get; set; }
public int SelectedState = 0;
}
public class Student
{
public int ID = 0;
public string Name = "";
public int StateID = 0;
public List<States> States { get; set; }
}
public class States
{
public int ID = 0;
public string Name = "";
}
now how could i bind dropdown with nested property called States of student class ?
#Html.DropDownListFor(x => x.SelectedState new SelectList(Model.Students.States, "ID", "Name", Model.SelectedState), "-- Select States--", new { id = "cboState", #class = "edit-mode" })
this is not working SelectList(Model.Students.States, "ID", "Name", Model.SelectedState) how to refer this Model.Students.States
please discuss this issue with code sample. each student has relation with states.

Check this out:
#Html.DropDownListFor(x => x.SelectedState, new SelectList(Model.Students.FirstOrDefault().States, "ID", "Name", Model.SelectedState), "-- Select States--", new { id = "cboState", #class = "edit-mode" })
But its better to save States List in ViewBag in your Controller:
ViewBag.States = (_context or _serviceLayer).(States or GetStates()).Select(s => new SelectListItem { Value = s.ID, Text = s.Name, Selected = s.ID == viewModel.SelectedState }).ToList();
And on View:
#Html.DropDownListFor(x => x.SelectedState, (List<SelectListItem>)ViewBag.States, "-- Select States--", new { id = "cboState", #class = "edit-mode" })
REQUESTED EDIT:
Models:
namespace MyProject.Models
{
public class ViewInfo
{
public int StateID { get; set; }
}
public class Student
{
public int ID { get; set; }
public string FullName { get; set; }
public int StateID { get; set; }
}
public class State
{
public int ID { get; set; }
public string Name { get; set; }
}
}
Controllers:
using MyProject.Models;
namespace MyProject.Controllers
{
public class StudentsController : Controller
{
private MyDBEntities _context;
public StudentsController()
{
this._context = new MyDBEntities();
}
// StudentsController
public ActionResult Index()
{
ViewBag.ViewInfo = new ViewInfo { StateID = 1 };
ViewBag.StateID = _context.States.Select(s => new SelectListItem { Value = s.ID, Text = s.Name }).ToList();
return View(_context.Students.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(ViewInfo viewInfo)
{
ModelState.Clear();
ViewBag.ViewInfo = viewInfo;
ViewBag.StateID = _context.States.Select(s => new SelectListItem { Value = s.ID, Text = s.Name, Selected = s.ID == viewInfo.StateID }).ToList();
return View(_context.Students.Where(s => s.StateID == viewInfo.StateID).ToList());
}
}
}
And the View:
#using MyProject.Models;
#model IEnumerable<MyProject.Models.Student>
#{
ViewBag.Title = "Students";
ViewInfo viewInfo = ViewBag.ViewInfo;
}
<div class="page-header">
<h2>Students</h2>
</div>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken();
#Html.DropDownList("StateID", null, new { #class = "form-control" })
<input type="submit" value="Filter" class="btn btn-primary" />
// Students Table
}

Related

Mapping problem when using Automapper in an Edit form with select list

I am trying to create an edit form which includes a selectlist that gets the data from the database. I am unable to display the form since I cannot map the viewmodel with the actual model using Automapper.
Contact.cs:
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string? EmailAddress { get; set; }
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
ContactEditViewModel.cs:
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string? EMailAddress { get; set; }
[Range(1, int.MaxValue, ErrorMessage = "Please select a company.")]
public int CompanyId { get; set; }
public SelectList? Company { get; set; }
Edit View
<div class="form-group">
<label asp-for="Company" class="control-label"></label>
<div class="input-group mb-3">
<select asp-for="CompanyId" class="form-select" asp-items="#Model.Company"></select>
</div>
</div>
ContactsController Edit Action:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var contact = await _context.Contacts.FirstOrDefaultAsync(c => c.ContactId == id);
var model = new ContactEditVM
{
Company = new SelectList(_context.Companies, "CompanyId", "CompanyName"),
};
//var contact = mapper.Map<ContactEditVM>(await contactRepository.GetAsync(id));
mapper.Map(model, contact);
if (contact == null)
{
return NotFound();
}
//ViewData["CompanyId"] = new SelectList(_context.Companies, "CompanyId", "CompanyName", contact.Company);
return View(model);
}
MappingConfiguration
public class MapConfig : Profile
{
public MapConfig()
{
CreateMap<Contact, ContactListVM>().ReverseMap();
CreateMap<Contact, ContactCreateVM>().ReverseMap();
CreateMap<Contact, ContactEditVM>().ReverseMap();
}
}
The error I get is:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
SelectList -> Company
Microsoft.AspNetCore.Mvc.Rendering.SelectList -> ENV.Data.Company
Destination Member:
Company
...
If I create a new instance of my viewmodel and assign values to it manually, without using Automapper, it works as intended. So what is wrong with my mapping?
Does it work if you outcomment the "Company" from your Contact.cs and outcomment the "Company" from your ContactEditViewModel.cs?
I think you need to define a mapping which tells autoMapper how to map a "SelectedList?" to a "Company".
For Example:
var autoMapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<WalletData/*Source*/, BP_WalletDTO/*Destination*/>()
.ForMember(dest => dest.Id, memberOptions => memberOptions.MapFrom(src => src.Id))
.ForMember(dest => dest.Type, memberOptions => memberOptions.MapFrom(src => src.Type))
.ForMember(dest => dest.Attributes, memberOptions => memberOptions.MapFrom(src => new BP_WalletAttributesDTO
{
CryptocoinId = src.Attributes.Cryptocoin_id,
CryptocoinSymbol = src.Attributes.Cryptocoin_symbol,
Balance = src.Attributes.Balance,
IsDefault = src.Attributes.Is_default,
Name = src.Attributes.Name,
PendingTransactionsCount = src.Attributes.Pending_transactions_count,
Deleted = src.Attributes.Deleted,
IsIndex = src.Attributes.Is_index,
}));
});
Maybe this helps

My Model is empty for ApplicationUser

I have model as
public class UserToRolesModel
{
public List<ApplicationUser> userList { get; set; }
public List<Microsoft.AspNet.Identity.EntityFramework.IdentityRole> rolesList { get; set; }
[Required]
public List<Microsoft.AspNet.Identity.EntityFramework.IdentityRole> selectedRoles { get; set; }
[Required]
public List<ApplicationUser> selectedUsers { get; set; }
}
And the view as
#model InventoryEasy15.Models.UserToRolesModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.ListBoxFor(x => x.selectedRoles, new SelectList(Model.rolesList, "Id", "Name"), new { style = "width:100%", id = "Roles", #class = "select2", data_placeholder = "Select the Role" })
#Html.ValidationMessageFor(m => m.selectedRoles, "", new { #class = "text-danger" })
And I am getting the Model is empty in Post. My post is
[HttpPost]
public ActionResult SaveUserRoles(UserToRolesModel usersroles)
{
foreach(var user in usersroles.selectedUsers)
{
var userExistingRoles = context.Roles;
}
return View(usersroles);
}
And the get is binding the data correctly,
public ActionResult AddUserToRole()
{
var model = new UserToRolesModel();
var users = context.Users.ToList();
model.userList = users;
model.rolesList = context.Roles.ToList();
return View(model);
}
Previously I had problem, where I used the model to local variable and it was causing the same issue. But I don't think there is a issue here

Model properties are null after submit

I have this model:
public partial class Group
{
public Group()
{
this.ParameterGroup = new HashSet<ParameterGroup>();
}
public string GroupId { get; set; }
public string Responsibility { get; set; }
public virtual Text GroupDescText { get; set; }
public virtual Text GroupNameText { get; set; }
public virtual ICollection<ParameterGroup> ParameterGroup { get; set; }
}
public partial class Text
{
public Text()
{
this.ParamName = new HashSet<Parameter>();
this.ParamDesc = new HashSet<Parameter>();
this.EnumElemName = new HashSet<EnumElem>();
this.IoDeviceInfoText = new HashSet<IoDeviceInfo>();
this.IoCatText = new HashSet<IoDeviceInfo>();
this.GroupDesc = new HashSet<Group>();
this.GroupName = new HashSet<Group>();
this.Type = new HashSet<Type>();
this.ParamDispPath = new HashSet<Parameter>();
this.EnumElemText = new HashSet<EnumElem>();
this.TextValue = new HashSet<TextValue>();
}
public string TextId { get; set; }
public string XmlId { get; set; }
public virtual ICollection<Parameter> ParamName { get; set; }
public virtual ICollection<Parameter> ParamDesc { get; set; }
public virtual ICollection<EnumElem> EnumElemName { get; set; }
public virtual ICollection<IoDeviceInfo> IoDeviceInfoText { get; set; }
public virtual ICollection<IoDeviceInfo> IoCatText { get; set; }
public virtual ICollection<Group> GroupDesc { get; set; }
public virtual ICollection<Group> GroupName { get; set; }
public virtual ICollection<Type> Type { get; set; }
public virtual ICollection<Parameter> ParamDispPath { get; set; }
public virtual ICollection<EnumElem> EnumElemText { get; set; }
public virtual ICollection<TextValue> TextValue { get; set; }
}
This is my Controller:
public class GroupController : Controller
{
// GET: Group
public ActionResult Index()
{
return PartialView("Index", GroupModel.Instance.getGroups());
}
public ActionResult Edit(string id)
{
Group group = KebaContext.SessionBasedContext().GroupSet.Where(g => g.GroupId == id).FirstOrDefault();
List<Language> langs = KebaContext.SessionBasedContext().LanguageSet.ToList();
foreach(Language l in langs)
{
if(group.GroupDescText == null)
{
group.GroupDescText = new Text();
TextValue value = new TextValue();
value.TextId = Guid.NewGuid().ToString("N");
value.LangId = l.LangId;
value.Value = "";
group.GroupDescText.TextValue.Add(value);
}
if (group.GroupNameText == null)
{
group.GroupNameText = new Text();
TextValue value = new TextValue();
value.TextId = Guid.NewGuid().ToString("N");
value.LangId = l.LangId;
value.Value = "";
group.GroupNameText.TextValue.Add(value);
}
if (group.GroupDescText != null && group.GroupDescText.TextValue.Where(x => x.LangId == l.LangId).FirstOrDefault() == null) //just one lang is available
{
TextValue value = new TextValue();
value.TextId = group.GroupDescText.TextValue.First().TextId;
value.LangId = l.LangId;
value.Value = "";
group.GroupDescText.TextValue.Add(value);
}
if (group.GroupNameText != null && group.GroupNameText.TextValue.Where(x => x.LangId == l.LangId).FirstOrDefault() == null) //just one lang is available
{
TextValue value = new TextValue();
value.TextId = group.GroupNameText.TextValue.First().TextId;
value.LangId = l.LangId;
value.Value = "";
group.GroupNameText.TextValue.Add(value);
}
}
return View(group);
}
[HttpPost]
public ActionResult Edit(Group xyz)
{
return RedirectToAction("Index", "Types");
}
}
This is my View:
#using System.Web.Mvc.Html;
#model Keba.Data.EF.Group
#{
ViewBag.Title = "Group Editing";
}
<h2>Edit Group</h2>
<div id="groupEdit">
#using (Html.BeginForm("Edit", "Group", FormMethod.Post))
{
#Html.HiddenFor(model => model.GroupId);
<table class="userEditAddTable">
<tr><th>Responsibility</th><td>#Html.EditorFor(model => model.Responsibility)</td></tr>
#foreach (var name in Model.GroupNameText.TextValue)
{
#Html.HiddenFor(model => name.LangId)
#Html.HiddenFor(model => name.Value)
<tr><th>GroupNameText(#Html.DisplayFor(model => name.LangId))</th><td> #Html.TextBoxFor(model => name.Value)</td></tr>;
}
#foreach (var desc in Model.GroupDescText.TextValue)
{
#Html.HiddenFor(model => desc.LangId)
#Html.HiddenFor(model => desc.Value)
<tr><th>GroupDescText(#Html.DisplayFor(model => desc.LangId))</th><td> #Html.TextBoxFor(model => desc.Value)</td></tr>;
}
</table>
<br />
<div id="buttons">
<input name="Save" type="submit" value="Save" class="button" />
<input name="Cancel" type="submit" value="Cancel" class="button" />
</div>
}
</div>
Problem:
If I try to change the value of a Text in the group model e.g. GroupNameText.TextValue.Value send it to the controller (submit). The properties GroupNameText and GroupDescText are null.
I have also tried the solution with propertybinding ([Bind(Include = "GroupDescText,GroupNameText")] Group xyz) which also doesn't work
First, remember that only properties that are posted (i.e. have a form input element representing them) will be populated.
Second, the names of the input elements must match up to what the model binder expects on post, or it will discard the values, as it won't know what to do with them. In particular, with enumerables, this means you need to use for loops rather than foreach, so that Razor can create the right name binding:
#for (var i = 0; i < Model.GroupNameText.TextValue; i++)
{
#Html.HiddenFor(m => m.GroupNameText.TextValue[i].LangId)
#Html.HiddenFor(m => m.GroupNameText.TextValue[i].Value)
...
}
That will result in a name attribute like GroupNameText.TextValue[0].LangId, which the model binder should be able to bind appropriately, whereas your field names are currently just LangId, which is meaningless on post.
Have a look at this similar to your approach is to have a list in the view, you might need to have partials.

dropdown population with viewmodel asp.net mvc

i am new in MVC. so when see code to understand then some time confusion occur. here i am giving a code. so please see the code first.
public class ProductViewModel
{
public int ID { set;get;}
public string Name { set;get;}
}
public class OrderViewModel
{
private List<ProductViewModel> _products;
public int OrderNumber { set; get; }
public List<ProductViewModel> Products
{
get
{
if (_products == null)
{
_products = new List<ProductViewModel>();
_products.Add(new ProductViewModel { ID = 1, Name = "Ketchup" });
_products.Add(new ProductViewModel { ID = 1, Name = "Mustard" });
_products.Add(new ProductViewModel { ID = 1, Name = "Relish" });
_products.Add(new ProductViewModel { ID = 1, Name = "Mayo" });
}
return _products;
}
}
public int SelectedProductId { set;get;}
}
public ActionResult Order()
{
OrderViewModel orderVM = new OrderViewModel();
return View(orderVM);
}
#model ORderViewModel
#using (Html.BeginForm())
{
<p>
#Html.DropDownListFor(x => x.SelectedProductId , new SelectList(Model.Products, "Value", "Text"), "-- Select Product--")
</p>
}
my question is can i place this code public int SelectedProductId { set;get;} in ProductViewModel instead of OrderViewModel.
if it is possible then what to change in code and in view html ?

Range Validation is not working

I have used range validation but this is not working. I am adding model,controller and view code.Please help(i have added only related fields only in this code).
Model is :
public class TicketDetailModel : TicketModelBase
{
public WorkOnTicketCreateModel WorkOnTicketCreateModel { get; set; }
}
public class TicketModelBase
{
[Required]
[Range(1, int.MaxValue, ErrorMessage = "Please enter a value bigger than {0}")]
public int EstimatedTime { get; set; }
public virtual List<WorkOnTicket> WorkOnTickets { get; set; }
}
public class WorkOnTicketCreateModel : WorkOnTicketModelBase
{
[Required]
[Display(Name = "AssignedToUser")]
public int AssignedToUserId { get; set; }
public IEnumerable<SelectListItem> AssignedUser { get; set; }
[Required]
[Display(Name = "Ticket Status")]
public int TicketStatusId { get; set; }
public TicketStatus TicketStatusVal { get; set; }
public IEnumerable<SelectListItem> TicketStatus { get; set; }
}
public class WorkOnTicketModelBase
{
public int Id { get; set; }
[Required]
public int EstimateHours { get; set; }
[Required]
[Range(1, int.MaxValue, ErrorMessage = "Please enter a value bigger than {0}")]
public int WorkedHours { get; set; }
}
Contoller:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Details(TicketDetailModel model, IEnumerable<HttpPostedFileBase> file)
{
using (ITransaction transaction = this.nhSession.BeginTransaction())
{
var ticketObj = this.nhSession.QueryOver<Ticket>().Where(t => t.Id == model.Id).SingleOrDefault();
var workOnTicket = new WorkOnTicket();
workOnTicket.Ticket = ticketObj;
workOnTicket.WorkedHours = model.WorkOnTicketCreateModel.WorkedHours;
workOnTicket.EstimateHours = model.WorkOnTicketCreateModel.EstimateHours;
ticketObj.WorkOnTickets.Add(workOnTicket);
this.nhSession.Save(ticketObj);
transaction.Commit();
}
return RedirectToAction("Details", new { id = model.Id, milestoneId = model.Milestone.Id, projectId = model.Project.Id });
}
View:-
#model AnkTech.TicketManagement.Web.Models.Ticket.TicketDetailModel
#using (Html.BeginForm("Details", "Ticket", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
#Html.TextBoxFor(model => model.WorkOnTicketCreateModel.EstimateHours, new { #id = "work_remaining", #class = "s-mini", size = "2" })
Worked hours: #Html.TextBoxFor(model => model.WorkOnTicketCreateModel.WorkedHours, new { #id = "worked_hours", #class = "s-mini", size = "2" })
<input type="submit" value="Submit" tabindex="2" name="commit" id="submit-comment"
class="gray-btn">
}
I have deleted all rmaining fields. i have added only fields to which related i am asking, please help.
You need to use ModelState.IsValid to check that the model is actually valid. Currently you assign validation attributes but never check them:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Details(TicketDetailModel model, IEnumerable file) {
if (!ModelState.IsValid)
{
// Handle error
}
else
{
using (ITransaction transaction = this.nhSession.BeginTransaction()) {
var ticketObj = this.nhSession.QueryOver<Ticket>().Where(t => t.Id == model.Id).SingleOrDefault();
var workOnTicket = new WorkOnTicket();
workOnTicket.Ticket = ticketObj;
workOnTicket.WorkedHours = model.WorkOnTicketCreateModel.WorkedHours;
workOnTicket.EstimateHours = model.WorkOnTicketCreateModel.EstimateHours;
ticketObj.WorkOnTickets.Add(workOnTicket);
this.nhSession.Save(ticketObj);
transaction.Commit();
}
}
return RedirectToAction("Details", new { id = model.Id, milestoneId = model.Milestone.Id, projectId = model.Project.Id });
}