MVC 3 Selectlist/DropDownList Is Not Updating My Model - asp.net-mvc-3

I hope someone can help with this one. I have three Model classes like this:
public class Provider
{
public Guid ProviderId { get; set; }
public string Name { get; set; }
public Guid LocationId { get; set; }
public virtual Location Location { get; set; }
}
public class Location
{
public Guid LocationId { get; set; }
public string NameOrCode { get; set; }
public string Description { get; set; }
public string StreetNumber { get; set; }
public string StreetAddress1 { get; set; }
public string StreetAddress2 { get; set; }
public string City { get; set; }
public int? StateId { get; set; }
public string Zip { get; set; }
public string ContactPhone { get; set; }
public virtual State State { get; set; }
}
public class State
{
public int StateId { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
As you can see, a Provider has a Location (separate class for reuse elsewhere), and a Location has a State (which is null until selected).
My Controller looks like this for my Create methods:
public class ProviderController : BaseController
{
private SetupContext db = new SetupContext();
// other CRUD methods ...
//
// GET: /Provider/Create
public ActionResult Create()
{
Location location = new Location()
{
LocationId = Guid.NewGuid(),
NameOrCode = Resources.BillingLocation,
Description = Resources.BillingLocationDescription
};
Provider provider = new Provider()
{
ProviderId = Guid.NewGuid(),
LocationId = location.LocationId,
Location = location
};
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
return View(provider);
}
//
// POST: /Provider/Create
[HttpPost]
public ActionResult Create(Provider provider)
{
if (ModelState.IsValid)
{
db.Locations.Add(provider.Location);
db.Providers.Add(provider);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
return View(provider);
}
// other CRUD methods ...
}
Finally, my View looks like this:
<div class="editor-label">
#Html.LabelFor(model => model.Location.StateId, #Resources.Location_State_Display_Name)
</div>
<div class="editor-field">
#Html.DropDownList("StateId", #Resources.ChooseFromSelectPrompt)
#Html.ValidationMessageFor(model => model.Location.StateId)
</div>
My problem is that the state the user selects in the DropDownList never gets set on my Model on the Create POST. I have similar code in my Edit View and the state is populated correctly in that View (that is, the state associated with an existing Provider.Location shows selected in the DropDownList for the user to edit if desire), but in both the Create and the Edit Views the selection made by the user is never registered in my Model (specifically the Provider.Location.StateId) coming in from the POST.
Looking at the HTML produced I see this:
<div class="editor-label">
<label for="Location_StateId">State/Territory</label>
</div>
<div class="editor-field">
<select id="StateId" name="StateId"><option value="">[Choose]</option>
<option value="1">Alabama</option>
<option value="2">Alaska</option>
<!-- more options ... -->
</select>
<span class="field-validation-valid" data-valmsg-for="Location.StateId" data-valmsg-replace="true"></span>
</div>
I suspect I need to somehow convey the Location.StateId relationship instead of just StateId as I see above but I can't figure out the correct syntax to do that. I've tried changing my ViewBag dynamic property to Location_StateId like this:
ViewBag.Location_StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
And the DropDownList in my View like this:
#Html.DropDownList("Location_StateId", #Resources.ChooseFromSelectPrompt)
I figured then perhaps that notation would work because the label beside my DropDownList was rendered as:
<div class="editor-label">
<label for="Location_StateId">State/Territory</label>
</div>
This attempt did not work. Can you help me out?
Thanks in advance.

#Html.DropDownList("Location.StateId", #Resources.ChooseFromSelectPrompt)
Also the following line doesn't do anything useful:
ViewBag.StateId = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
You are assigning a SelectList to something that is supposed to be a scalar property. You probably wanted to pass the collection as ViewBag:
ViewBag.States = new SelectList(db.States, "StateId", "Name", provider.Location.StateId);
and then in the view:
#Html.DropDownList("Location.StateId", (SelectList)ViewBag.States)

Related

While filtering, how to POST a SearchModel, but GET a different ResultsModel back in the view

I am creating a filter view to find records. This example on SO helps, but does not mention how handle the (Filtered) View.
The err below is because, the actions returns a List<ProductViewModel>, and it Errors/complains that the View is using a SearchViewModel, I need to this POST the searchmodel/variables, butGET back the list/results model
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[ViewModels.ProductVM]', but this
dictionary requires a model item of type 'ViewModels.SearchModel'.
Issue/Question: Since there are two models, the SearchViewModel passed to the controller & the ProductViewModel returned as a result, which model should be strongly typed to the view? and How can I create the view to handle both SearchModel & ProductModel If I stronglyType ProductVM, then I loose the submitform from the SearchVM.
I create the SearchView as the mainview, & the _ResultsPartialView as a partialView, is this wrong?
public ActionResult Index(SearchModel searchModel)
{
var filteredProdVMList = _Repository.GetFilteredProducts(searchModel);
return View(filteredProdVMList);
}
public class ProductVM
{
public int Id { get; set; }
public int Price { get; set; }
public string Name { get; set; }
// implicit const... blah.. removed
}
public class SearchModel
{
public int? Id { get; set; }
public int? PriceFrom { get; set; }
public int? PriceTo { get; set; }
public string Name { get; set; }
}
You need to modify your SearchModel to include a collection property for the products
public class SearchModel
{
public int? PriceFrom { get; set; }
public int? PriceTo { get; set; }
....
public IEnumerable<ProductVM> Products { get; set; } // add
}
then you return just SearchModel to your view
public ActionResult Filter(SearchModel filter)
{
filter.Products = _repository.GetFilteredProducts(filter);
return View(filter);
}
and your view will be
#model SearchModel
....
#using (Html.BeginForm("Filter", "yourControllerName", FormMethod.Get))
{
#Html.LabelFor(m => m.PriceFrom)
#Html.EditorFor(m => m.PriceFrom)
#Html.ValidationMessageFor(m => m.PriceFrom)
... // other form controls for properties you want to filter the results on
<input type="submit" value="Filter" />
}
#Html.Partial("_ResultsPartialView", Model.Products)

Razor - Passing DropDownList selected value to Controller via Ajax.BeginForm

I am trying to pass the dropdownlist value to my controller so I can add the values to a list of paycodes but I keep getting a "Value cannot be null." error. My paycode list has many items in it. Not sure what is null or wrong here...
INNER EXCEPTION
Value cannot be null.
Parameter name: source
VIEW
<!-- products input-->
<div class="control-group col-lg-6">
<label class="control-label">Product</label>
<div class="controls">
#using (Ajax.BeginForm("AddPayCode", "Referral",
new AjaxOptions()
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "PayCodes",
Url = Url.Action("AddPayCode", "Referral")
}))
{
#Html.ValidationSummary()
#Html.DropDownListFor(model => model.SelectedPayCode, new SelectList(Model.PayCodes.ToList(), "Id", "Description"), "- Select -")
<input type="submit" value="Add" />
}
</div>
</div>
CONTROLLER
[HttpPost]
public void AddPayCode(ReferralModel model)
{
var test = model.SelectedPayCode;
//TODO: Add to model.Referral.PayCodes list and return list of selected items
}
MODEL
public class ReferralModel
{
public Customer Customer { get; set; }
public Employee Employee { get; set; }
public List<PayCode> PayCodes { get; set; }
public int SelectedPayCode { get; set; }
public Referral Referral { get; set; }
}
DOMAIN OBJECT
public class Referral
{
[Key]
public int Id { get; set; }
public int CustomerId { get; set; }
public int EmployeeId { get; set; }
public decimal Total { get; set; }
public virtual List<PayCode> PayCodes { get; set; }
public virtual Customer Customer { get; set; }
public virtual Employee Employee { get; set; }
}
Objective :
User chooses a paycode from the dropdown and clicks "Add" Paycode is
added to the referral PayCodes list
Controller returns the list of paycodes selected to the view (not
yet implemented)
The issue was the controller is missing the property of the dropdown list.
[HttpPost]
public void AddPayCode(ReferralModel model, ** string SelectedPayCode ** <-- missing)
{
var test = SelectedPayCode;
//TODO: Add to model.Referral.PayCodes list and return list of selected items
}

DropDownList Result not being Returned

I am trying to create an Item that has an ItemType coming from another table. I am unable to get back the actual Type object from the Create page. This is the code I have tried:
Models:
public class ItemType {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Item> Item{ get; set; }
}
public class Item {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ItemType ItemType { get; set; }
}
In the ItemController, this is my create code:
public ActionResult Create() {
var itemTypeRepo = new ItemTypeRepository(context);
ViewBag.ItemTypes = new SelectList(itemTypeRepo.GetAll(), "ID", "Name");
return View();
}
[HttpPost]
public ActionResult Create(Item item) {
if (ModelState.IsValid) {
context.Items.Add(item);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
In my Create.cshtml view I have tried:
<div class="editor-field">
#Html.DropDownList("ItemType", String.Empty)
#Html.ValidationMessageFor(model => model.ItemType)
</div>
This returns no value at all and throws an error "The value 'X' is invalid." Where X is the ID of the ItemType I selected.
And
<div class="editor-field">
#Html.DropDownListFor(x => x.ItemType.Id, (SelectList)ViewBag.ItemType)
#Html.ValidationMessageFor(model => model.ItemType)
</div>
This creates a stub ItemType object with the correct ID but does not insert it into the database since the object is not fully loaded. If I look at ModelState object, I find that there is an error that the Name field is missing from ItemType object.
I also attempted to solve the problem using the second .cshtml code and adding this code:
public ActionResult Create(Item item) {
item.ItemType = context.ItemTypes.Find(item.ItemType.Id);
if (ModelState.IsValid)
This does not change the value of ModelState.IsValid from false even through it should.
What do I need to do to get this to work?
You should add a property ItemTypeId to your Item entity so that it acts as a foreign key.
public class Item
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int ItemTypeId { get; set; }
[ForeignKey("ItemTypeId")]
public virtual ItemType ItemType { get; set; }
}
You can then use that property for the dropdownlist:
<div class="editor-field">
#Html.DropDownListFor(x => x.ItemTypeId, (SelectList)ViewBag.ItemType)
#Html.ValidationMessageFor(model => model.ItemType)
</div>

ViewBag multiple SelectList for dropdown list

I am trying to use dropdownList with two foreign keys which are modelId, and categoryId.
And I am using ViewBag with selectList.
public ActionResult Create()
{
ViewBag.categoryId = new SelectList(db.Category, "categoryId", "name");
ViewBag.modelId = new SelectList(db.Model, "modelId", "name");
return View();
}
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Product.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.categoryId = new SelectList(db.Category, "categoryId", "name", product.categoryId);
ViewBag.modelId = new SelectList(db.Model, "modelId", "name", product.modelId);
return View(product);
}
And here is my Create.cshtml.
<div class="editor-label">
#Html.LabelFor(model => model.Category)
</div>
<div class="editor-field">
#Html.DropDownList("categoryId", "--Select--")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Model)
</div>
<div class="editor-field">
#Html.DropDownList("modelId", "--Select--")
</div>
When I press submit button, error come up,
'An item with the same key has already been added'
What is problem? Is it problem with in Model?
Here is my models.
--Prodruct.cs--
public class Product
{
[Key] public int productId { get; set; }
[Required(ErrorMessage = "Please select category")]
public int categoryId { get; set; }
[Required(ErrorMessage = "Please select model")]
public int modelId { get; set; }
[DisplayName("Model name")]
public String model { get; set; }
public virtual Category Category { get; set; }
public virtual Model Model { get; set; }
}
--Category.cs--
public class Category
{
[Key] public int categoryId { get; set; }
public String name { get; set; }
}
--Model.cs--
public class Model
{
[Key] public int modelId { get; set; }
public String name { get; set; }
}
--RentalDB.cs--
public class rentalDB : DbContext
{
public DbSet<Product> Product { get; set; }
public DbSet<Model> Model { get; set; }
public DbSet<Customer> Customer { get; set; }
public DbSet<Order> Order { get; set; }
public DbSet<Cart> Cart { get; set; }
public DbSet<Category> Category { get; set; }
public DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Where it is wrong? Index page in Create can get category data and model data. However, when I submit it, it has error, 'An item with the same key has already been added'.
Could you help me where has got problem?
Thank you.
--added more coding--
I am using this LINQ. Probably here has problem.
How can I add 'Model' entity in here?
var product = from a in db.Product.Include(a => a.Category)
select a;
This is how I would have done it..
I would suggest that you don't send your domain models to the view, but rather create a view model for each view. Doing it this way you will only include what is needed on the screen.
Create a new view model for your Create view:
public class ProductCreateViewModel
{
// Include other properties if needed, these are just for demo purposes
public string Name { get; set; }
public string SKU { get; set; }
public string LongDescription { get; set; }
// This is the unique identifier of your category,
// i.e. foreign key in your product table
public int CategoryId { get; set; }
// This is a list of all your categories populated from your category table
public IEnumerable<Category> Categories { get; set; }
// This is the unique identifier of your model,
// i.e. foreign key in your product table
public int ModelId { get; set; }
// This is a list of all your models populated from your model table
public IEnumerable<Model> Models { get; set; }
}
Category class:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
Model class:
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
}
In your Create view you would have the following:
#model MyProject.ViewModels.ProductCreateViewModel
#using (Html.BeginForm())
{
<table>
<tr>
<td><b>Category:</b></td>
<td>
#Html.DropDownListFor(x => x.CategoryId,
new SelectList(Model.Categories, "Id", "Name", Model.CategoryId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.CategoryId)
</td>
</tr>
<tr>
<td><b>Model:</b></td>
<td>
#Html.DropDownListFor(x => x.ModelId,
new SelectList(Model.Models, "Id", "Name", Model.ModelId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.ModelId)
</td>
</tr>
</table>
<!-- Add other HTML controls if required and your submit button -->
}
Your Create action methods:
public ActionResult Create()
{
ProductCreateViewModel viewModel = new ProductCreateViewModel
{
// Here you do database calls to populate your dropdowns
Categories = categoryService.GetAllCategories(),
Models = modelService.GetAllModels()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(ProductCreateViewModel viewModel)
{
// Check that viewModel is not null
if (!ModelState.IsValid)
{
viewModel.Categories = categoryService.GetAllCategories();
viewModel.Models = modelService.GetAllModels();
return View(viewModel);
}
// Mapping
Product product = ... // Do your mapping here
// Insert product in database
productService.Insert(product);
// Return the view where you need to be
}
I would also recommend that you use AutoMapper to do the mappings for you between your domain model and view model. I would also recommend that you look at Fluent Validation to take care of your view model validations.
I hope this helps.
UPDATED ANSWER
The service that was used to get all the categories could look like this:
public class CategoryService : ICategoryService
{
private readonly ICategoryRepository categoryRepository;
public CategoryService(ICategoryRepository categoryRepository)
{
// Check if category repository is not null, throw exception if it is
this.categoryRepository = categoryRepository;
}
public IEnumerable<Category> GetAllCategories()
{
return categoryRepository.GetAllCategories();
}
}
categoryRepository is injected by Autofac.
Category service interface:
public interface ICategoryService
{
IEnumerable<Category> GetAllCategories();
}
I currently still use Entity Framework 4.1 code first.
My category repository:
public class CategoryRepository : ICategoryRepository
{
MyContext db = new MyContext();
public IEnumerable<Category> GetAllCategories()
{
return db.Categories
.OrderBy(x => x.Name);
}
}
My category repository interface:
public interface ICategoryRepository
{
IEnumerable<Category> GetAllCategories()
}
public class Test
{
rentalDB db = new rentalDB();
public Product LoadProductById(int pId)
{
return db.Products.Include(p => p.Model).Include(p => p.Category).Where(p => p.productId == pId).SingleOrDefault();
} // To get specific product.
public IEnumerable<Product> LoadAllProducts()
{
return db.Products.Include(p => p.Model).Include(p => p.Category).ToList();
} // To get all products.
}
I have changed your DbSet to Products make it more clear. This is how you load all the references for one product or all products, in order to iterate over them.

How to make dropdownlist

How make a dropdownlist? [Edited - almost working code]
View:
<div class="editor-label">
Firma
<div class="editor-field">
#Html.DropDownListFor(x => x.ID_firma, Model.firmaList)
#Html.ValidationMessageFor(model => model.nazwa)
</div>
Model:
public class produktModel
{
[Required(ErrorMessage="Proszę podać nazwę.")]
public string nazwa { get; set; }
[Required(ErrorMessage = "Proszę podać ilść produktu.")]
public decimal ilosc { get; set; }
[Required(ErrorMessage = "Proszę podać jednostkę produktu (np. kg, l, szt.).")]
public string jednostka { get; set; }
[Required(ErrorMessage = "Proszę podać cenę produktu.")]
public decimal cena { get; set; }
public string ID_firma { get; set; }
public IEnumerable<SelectListItem> firmaList { get; set; }
}
Controller:
public ActionResult dodaj()
{
var firma = baza.Firmas;
var model = new produktModel
{
firmaList = firma.AsEnumerable().Select(x => new SelectListItem
{
Value = x.ID_firma.ToString(),
Text = x.nazwa
})
};
return View(model);
}
[HttpPost]
public ActionResult dodaj(produktModel model)
{
Produkt prod = new Produkt();
prod.nazwa = model.nazwa;
prod.ilosc = model.ilosc;
prod.jednostka = model.jednostka;
prod.cena = model.cena;
prod.ID_firma = model.ID_firma;
baza.Produkts.InsertOnSubmit(prod);
baza.SubmitChanges();
return RedirectToAction("zarzadzaj_produktami", "Produkt");
}
It almost work...
I have only one problem (I hope)...
Value is string, and I save his value to database... (I don't now how to write it...)
prod.ID_firma = model.ID_firma;
prod.ID_firma is int. model.ID_firma is this value which is string. So I have an error:
Error 1 Cannot implicitly convert type 'string' to 'int?'
change your model a bit, i have assumed the column names change them according to your code
public class produktModel
{
[Required]
public string name { get; set; }
public decimal price { get; set; }
[Required]
public int companyID {get; set;}
public List<Company> compList {get; set;}
}
public class Company{
public int CompanyID {get;set;}
public string CompanyName {get;set;}
}
ActionResult should look like
public ActionResult add()
{
produktModel model = new produktModel();
model.compList= (from b in base.Companies
select new Company{
CompanyID = b.CompanyID,
CompanyName = b.CompanyName
}).ToList();
return View(model);
}
in your (strongly typed) view
#model produktModel
....
<div class="editor-label">
Company
<div class="editor-field">
#Html.DropDownListFor(model => model.companyID,
new SelectListItem(model.compList,
"CompanyID ",
"CompanyName "))
#Html.ValidationMessageFor(model => model.company_name)
</div>
...
Your question isn't clear enough.
Any way you can use the telerik combox\dropdown list or the default mvc dropdown list. you can find a lot of examples on google for that.
With Telerik write something like this:
#(Html.Telerik().ComboBox()
.Name("ComboBox")
.BindTo(new SelectList("CompanyID", "CompanyName")))
see this Telerik demo for more information.

Resources