MVC Razor two level Model for ListBoxFor - asp.net-mvc

Is there a way in the view to drill down one level to get the data object in the GenericValues? I know that I can change the model around to make it work but I didnt want to add extra model classes?
View
<div class="form-group row">
#Html.LabelFor(model => model.selectedLanguage)
#Html.ListBoxFor(
x => x.selectedLanguage,
new SelectList(Model.ListOfLanguages, "Value.Value", "Value.Name"),
new { id = "multiselectLanguage", #class = "form-control" })
</div>
Model
public List<LanguageModel> ListOfLanguages { get; set; }
public class LanguageModel
{
public string ISOCode { get; set; }
public GenericValues Values { get; set; }
}
public class GenericValues
{
public string Name { get; set; }
public int Value { get; set; }
}

You can use linq to get list<GenericValues>:
#Html.ListBoxFor(
x => x.selectedLanguage,
new SelectList(Model.ListOfLanguages.Select(x => x.Values).Distinct(), "Value", "Name"),
new { id = "multiselectLanguage", #class = "form-control" })

Related

Problem in showing ViewModel in Create form

I am learning how to use ViewModel to show the fields from 2 different models. I have one model containing the MsgTypeId, MsgType and MsgStatus and the another model OptStatus containing the StatusId, StatusName and StatusValue. The MsgStatus will be shown in form of drop down list and show all the values in OptStatus. Both models have a separate database table to store their values.
namespace theManager.Areas.Settings.Models
{
public class OptStatus
{
[Required]
[Key]
public int StatusId { get; set; }
[Required]
public string StatusName { get; set; }
[Required]
public char StatusValue { get; set; }
}
}
namespace theManager.Areas.Settings.Models
{
public class OptMsgType
{
[Required]
[Key]
public int MsgTypeId { get; set; }
[Required]
public string MsgType { get; set; }
[Required]
public string MsgStatus { get; set; }
}
}
I have created a ViewModel to show these fields in the Create form of OptMsgType. However, when I run the code, I got an error
"System.NullReferenceException: 'Object reference not set to an instance of an object.'"
I would like to ask if there is something wrong with my ViewModel. Thanks!
namespace theManager.Areas.Settings.ViewModels
{
public class OptMsgTypeCreateViewModel
{
public OptMsgType OptMsgType { get; set; }
public IEnumerable<SelectListItem> OptStatuses { get; set; }
}
}
OptMsgTypeController.cs
public IActionResult Create(int id)
{
var OptMsgTypeViewModel = new OptMsgTypeCreateViewModel();
OptMsgTypeViewModel.OptStatuses = _context.OptStatus.ToList().Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
});
OptMsgTypeViewModel.OptMsgType = _context.OptMsgType.Where(a => a.MsgTypeId == id).FirstOrDefault();
//var v = _context.OptMsgType.Where(a => a.MsgTypeId == id).FirstOrDefault();
return View(OptMsgTypeViewModel);
}
I have problems in displaying the Create form which will show the fields declared in the ViewModel.
#model theManager.Areas.Settings.ViewModels.OptMsgTypeCreateViewModel
#{
ViewData["Title"] = "Create";
Layout = null;
}
<h2>Message Type Settings</h2>
#using (Html.BeginForm("Create","OptMsgType", FormMethod.Post, new { id= "popupForm" }))
{
if (Model != null && Model.OptMsgType.MsgTypeId > 0)
{
#Html.HiddenFor(a=>a.OptMsgType.MsgTypeId)
}
<div class="form-group">
<label>Message Type ID</label>
#Html.TextBoxFor(a=>a.OptMsgType.MsgTypeId,new { #class = "form-control" })
#Html.ValidationMessageFor(a=>a.OptMsgType.MsgTypeId)
</div>
<div class="form-group">
<label>Leave Type</label>
#Html.TextBoxFor(a => a.OptMsgType.MsgType, new { #class = "form-control" })
#Html.ValidationMessageFor(a => a.OptMsgType.MsgType)
</div>
<div class="form-group">
<label>Status</label>
#Html.DropDownListFor(model => model.OptStatuses, new SelectList(Model.OptStatuses, "Value", "Text"), htmlAttributes: new { #class = "form-control", id = "OptStatus" })
#Html.ValidationMessageFor(a => a.OptStatuses)
</div>
<div>
<input type="submit" value="Create" />
</div>
}
The System.NullReferenceException indicates that you are using a field without initializing it. It coulbe a problem with your view model or it could be a problem anywere else. For example from the code smaple is not possible to see where you initialize the context you are using to get the data, and that could be the cause of the exception you are getting.
Either way I would advise you to pay attention to yout IDE, it usualy indicates in which line adnd class the exception is being thown. If you navigate to that class at that line you will easily identify which field can be de cause of the exception.
Regarding your view model, its considered a good practice to always initialize the lists on your model on the constructor of your class. This way you can guarantee that they are already initialized when you try to use them.
So my sugestion would be to initialize your list on the constructor of your viewmodel
public OptMsgTypeCreateViewModel()
{
OptStatuses = new List<OptStatus>();
}
#George, thanks for the reply. Please try this then: instantiate your class in the viewmodel.
public class OptMsgTypeCreateViewModel
{
public OptMsgTypeCreateViewModel()
{
OptMsgType = new OptMsgType();
}
public OptMsgType OptMsgType { get; set; }
public IEnumerable<SelectListItem> OptStatuses { get; set; }
}
hi in action controller you should change this code:
OptMsgTypeViewModel.OptStatuses = _context.OptStatus.ToList().Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
});
I think _context.OptStatus.ToList() in null so you get this exception. change code to this:
OptMsgTypeViewModel.OptStatuses =new list<SelectListItem>();
var temp= _context.OptStatus.ToList();
if(temp!=null&&temp.count()>0)
{
OptMsgTypeViewModel.OptStatuses = temp.Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
}).tolist();
}
EDIT:
I think this object "Model.OptMsgType" is null
change code in view like this:
if (Model != null && Model.OptMsgType!=null && Model.OptMsgType.MsgTypeId > 0)
{
#Html.HiddenFor(a=>a.OptMsgType.MsgTypeId)
}

Asp.Net MVC dropdownlist not posting value to controller -> database

my development environment is ASP.NET MVC 5 using Entity Framework 6 using a code-first workflow.
My problem: When trying to add a new record to the database, the values from my two dropdownlists are not being included. All the other fields are indeed saving to the database.
I am passing a ViewModel to the view in question:
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public Item Item { get; set; }
public IEnumerable<Donor> Donors { get; set; }
}
The domain models I am using:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public bool IsActive { get; set; }
public Category Category { get; set; }
public Donor Donor { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Donor
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
public string Email { get; set; }
}
The relevant controller: This New() action is simply used to pass data to the view for the form to be submitted
public ActionResult New()
{
var itemCategories = _context.ItemCategory.ToList();
var donors = _context.Donors.ToList();
var viewModel = new NewItemViewModel
{
Categories = itemCategories,
Donors = donors
};
return View(viewModel);
}
Here is my actual action that adds the submitted data to the database:
// Create a new item.
[HttpPost]
public ActionResult Create(Item item)
{
_context.Items.Add(item);
_context.SaveChanges();
return RedirectToAction("Index", "Item");
}
And lastly, the view itself (I will minimize the code here):
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.Item.Donor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.Item.Category, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}
To reiterate the problem I am having: The values of the dropdownlist for Category and Donor are not saving to the database, whereas the others (non-navigation properties?) Name, Description, Quantity, etc. is working correctly.
Am I using the ViewModel the right way? I was under the impression that the MVC framework knows how to deal with passing the Item object parameter in the Create() action - mapping what it needs to within the Item entity.
Any help would be much appreciated. Thank you.
In the NewItemViewModel you have not created property to hold the selected values from dropdown
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public int SelectedCategory{get;set;}
public Item Item { get; set; }
public int SelectedDonor{get;set;}
public IEnumerable<Donor> Donors { get; set; }
}
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.SelectedDonor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.SelectedCategory, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}

Custom ValidationAttribute for list Does not stop form from submitting

I'm attempting client side validation of a list. I want at least one item to be in the list.
I'm using the solution found here:
ViewModel validation for a List
The ValidationAttribute is being called, and it is returning false when the list is empty or null, but the form is still submitting.
Model:
public class ProductsViewModel
{
UniformRepository repo;
public ProductsViewModel()
{
repo = new UniformRepository();
Products = new List<Product>();
ProductToEdit = new Product();
Divisions = repo.GetAllDivisions();
}
public List<Product> Products { get; set; }
public Product ProductToEdit { get; set; }
public Division SelectedDivision { get; set; }
public List<SelectListItem> DropDownVendors { get; set; }
public List<Division> Divisions { get; set; }
[EnsureOneElement(ErrorMessage = "At least one vendor is required")]
public List<string> Vendors { get; set; }
}
part of the View in question:
<div class="row top-buffer">
<div class="col-md-3 col-md-offset-1">
#Html.LabelFor(model => model.Vendors, new { #class = "GridLabel" })
</div>
<div class="col-md-3">
#(Html.Kendo().MultiSelectFor(model => model.Vendors)
.BindTo(Model.DropDownVendors)
.HtmlAttributes(new { #class = "form-control" }))
#Html.ValidationMessageFor(model => model.Vendors, "", new { #class = "text-danger" })
</div>
</div>
ValidationAttribute
public class EnsureOneElementAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var list = value as IList;
if (list != null)
{
return list.Count > 0;
}
return false;
}
}
Thanks for any ideas.

Error: There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Customer'

I need to get all the customers from a different table that is customers into Tickets form.
My Modal....
public class Ticket
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required()]
public Int32 TicketID { get; set; }
public Int32 TicketNum { get; set; }
[Required()]
public DateTime TicketDate { get; set; }
[ForeignKey("Customer")]
public Int32 CustomerID { get; set; }
[Required(ErrorMessage ="Customer Name is required!!")]
[Display(Name ="Customer Name")]
public String CustomerName { get; set; }
public Int32 FieldID { get; set; }
public Int32 WellID { get; set; }
public Int32 WellName { get; set; }
public Int32 LogID { get; set; }
public IEnumerable<SelectListItem> Customers { get; set; }
public virtual Customer Customer { get; set; }
// public virtual Field Field { get; set; }
// public virtual Well Well { get; set; }
// public virtual Log Log { get; set; }
}
My Controller...
[HttpPost]
public ActionResult Create(Ticket ticket)
{
//Very important step
if (!ModelState.IsValid)
{
return View(ticket);
}
Console.WriteLine("Customer: ");
ViewBag.Customers = new SelectList(context.Customers.ToList());
try {
context.Tickets.Add(ticket);
context.SaveChanges();
}
catch(Exception ex)
{
ModelState.AddModelError("Error:" , ex.Message);
return View(ticket);
}
TempData["Message"] = "Created " + ticket.TicketID;
return RedirectToAction("Index");
}
My View.....
<div class="form-group">
#Html.LabelFor(model => model.TicketDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TicketDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TicketDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, "Customer", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Customers", (IEnumerable<SelectListItem>)(ViewBag.Customers), htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
I know there are number of posts on this and I tried everyone of them.
Firstly you cannot name the ViewBag property the same as the property your binding to, but it appears you want to bind to CustomerID anyway, so your view should be (always use the strongly typed HtmlHelper methods)
#Html.DropDownListFor(m => m.CustomerID, (IEnumerable<SelectListItem>)(ViewBag.Customers), htmlAttributes: new { #class = "form-control" }
Next, the reason you get the exception is because ViewBag.Customers is null. You must reassign the value of the the ViewBag property if you return the view in the POST method. Assuming Customers contains properties CustomerID and CustomerName, then the code in the GET method needs to be
ViewBag.Customers = new SelectList(context.Customers, "CustomerID", "CustomerName");
and in the POST method
if (!ModelState.IsValid)
{
// Reassign the SelectList
ViewBag.Customers = new SelectList(context.Customers, "CustomerID", "CustomerName");
return View(ticket);
}
Console.WriteLine("Customer: ");
// ViewBag.Customers = new SelectList(context.Customers.ToList()); Remove this
try {
....
Finally, based on the view you have shown, you need to remove property public string CustomerName { get; set; } which has a [Required] attribute, meaning ModelState will always be invalid. You have a dropdownlist for selecting the customer which needs to bind to CustomerID and you do not have a control for binding to CustomerName (and nor should you have one)

DropDownList MVC returns null

I've encountered some problems using DropDownList in ASP.NET MVC lately.
I want to save value of selected item to member called Wydzialy.
Sorry for not translating some names, they doesn't matter I think :)
Here is what I have:
View:
<div class="form-group">
#Html.LabelFor(model => model.Wydzial, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.Wydzial, (List<SelectListItem>)ViewBag.Wydzialy)
</div>
</div>
Model:
public class Student
{
public int Id { get; set; }
public int NumerIndeksu { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public int Semestr { get; set; }
public virtual Wydzial Wydzial { get; set; }
}
Controller:
public ActionResult Create()
{
var wydzialy = db.Wydzialy.ToList();
var lista = wydzialy.Select(W => new SelectListItem()
{
Text = W.Nazwa
}).ToList();
ViewBag.Wydzialy = lista;
return View();
}
Your trying to bind the dropdown to a complex object. A <Select> only posts backs a value type (in your case the text of the selected option).
Either bind to a property of Wydzial
#Html.DropDownListFor(x => x.Wydzial.Nazwa, (List<SelectListItem>)ViewBag.Wydzialy)
or preferably use a view model that includes a property to bind to and the SelectList
public class StudentVM
{
public int Id { get; set; }
// Other properties used by the view
public string Wydzial { get; set; }
public SelectList Wydzialy { get; set; }
}
Controller
public ActionResult Create()
{
StudentVM model = new StudentVM();
model.Wydzialy = new SelectList(db.Wydzialy, "Nazwa", "Nazwa")
return View(model );
}
View
#model StudentVM
....
#Html.DropDownListFor(x => x.Wydzial, Model.Wydzialy)
Note you appear to be binding only to the Nazwa property of Wydzial. Typically ou would display a text property but bind to an ID property.

Resources