I have Entity Framework model which contains reference to other Entity like
public class Product
{
[Key]
public int ProductID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public virtual Shop Shop { get; set; }
[Required]
public double Price { get; set; }
}
I would like to create Edit View which contain Shop selector (DropDown).
By default I have created basic MVC Controller with Entity model connected, which created Edit like:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Product product = db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
return View(product);
}
and View does not contain Shop selector.
I have tried to add DropDown like:
#Html.DropDownListFor(product => product.Shop, (SelectList)ViewBag.Shops)
But in POST method, Shop entity is null.
How to handle that?
Create a view model to represent what you want to display
public class ProductVM
{
public int ProductID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int? ShopID { get; set; }
[Required]
public double Price { get; set; }
public SelectList ShopList { get; set; }
}
and in your controller, map your model to the view model
public ActionResult Edit(int? ID)
{
....
Product product = db.Products.Find(id);
ProductVM model = new ProductVM();
// map properties
....
// populate select list (assumes Shop has properties ID and Name)
model.ShopList = new SelectList(db.Shops, "ID", "Name");
return View(product);
}
and in your view
#model ProductVM
....
#Html.DropDownListFor(m => m.ShopID, Model.ShopList, "--Select shop--")
#Html.ValidationMessageFor(m -> m.ShopID)
this will post back the model with the selected ID of the Shop. Select controls post back single values so you cannot post back a complex object such as Shop. The POST method would be
[HttpPost]
public ActionResult Edit(ProductVM model)
{
....
}
Note you can use tools such as automapper to make mapping easier
I hope this helps.
Model for Product:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public int ShopID { get; set; }
public double Price { get; set; }
}
Then a ViewModel for Product:
public class ProductViewModel
{
public Product Model { get; set; }
public IEnumerable<SelectListItem> Shops{ get; set; }
public ProductViewModel()
{
GetShops();
}
public void GetShops()
{
Shops = new List<SelectListItem>();
var collectionShops = GetShopsFromDatabase();
Shops.AddRange(
collectionShops.Select(
contract =>
new SelectListItem
{
Text = contract.ShopDescription,
Value = contract.ShopID.ToString()
}));
}
}
In your View:
#model ProductViewModel
....
#Html.DropDownListFor(x => x.Model.ShopID, Model.Shops, new { #title = "Please select a shop" })
Related
So, i am new to programming and i am trying to make my first .net project, and i'm stuck.
I have a database that contains table Product, it looks like this:
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
[Range(1, int.MaxValue)]
public double ProductPrice { get; set; }
public string ProductDescription { get; set; }
public string ProductImagePath { get; set; }
public int ProductColorId { get; set; }
[ForeignKey("ProductColorId")]
public virtual ProductColor ProductColor { get; set; }
public int ProductShippingOptionId { get; set; }
[ForeignKey("ProductShippingOptionId")]
public virtual ProductShippingOption ProductShippingOption { get; set; }
public int ProductConditionId { get; set; }
[ForeignKey("ProductConditionId")]
public virtual ProductCondition ProductCondition { get; set; }
Columns ProductShippingOption, ProductColor and ProductCondition are a separate tables that each contain columns for Id and Name.
When i add a product to database, i want to show details of just one product in a view, but i need to display ProductConditionName instead of ProductConditionId (for example).
What should i include in my ViewModel and my Controller so i can use it in my View?
My action in a ProductController looks like this
public IActionResult ProductTemplate(int? id)
{
ProductVM productVM = new ProductVM()
{
Product = _db.Product.Find(id),
};
if (id == 0)
{
return NotFound();
}
if (id == 0)
{
return NotFound();
}
if(productVM==null)
{
return NotFound();
}
return View(productVM);
}
Thanks!
Best and easiest way to do it is including the classes in Product:
For your ProductTemplate action :
ProductVM productVM = new ProductVM()
{
Product = _db.Product.Where(s=>s.Id == id)
.Include(s=>s.ProductColor)
.Include(s=>s.ProductShippingOption)
.Include(s=>s.ProductCondition)
.FirstOrDefault();
};
And you can call them in your .cshtml with (Let say you want ProductColor name) :
#Model.Product.ProductColor.Name
Alternatively you can add Include() to your context to take all includes defaultly.
I have a class for which has a 1 to many relationship with another class. for this I will use class Car and class Gears. I need to create a form, which registers a car and the user needs to specify a choice of gears.
public class Car
{
public int id { get; set; }
public string desc { get; set; }
public List<Gear> Gears { get; set; }
}
public class Gear
{
public int gid { get; set; }
public int gname { get; set; }
}
using asp.net MVC 5, I have a create form, which I have scaffolded to the Car model, and within the form, I wish to have a checkboxlist of gears,
I also have a ViewModel that I have provided for my checkboxlist which is as below:
public class GearsViewModel
{
public Gear _gear {get; set; }
public bool _isChecked {get; set;}
}
Controller looks like:
Gears fetched from db context will be
"GearR","Gear1","Gear2","Gear3","Gear4","Gear5","Gear6","Gear7"
public action Create()
{
ViewBag.Gears = new SelectList(db.Gears, "gid","gname");
List<GearViewModel> _gears= new List<GearViewModel>();
foreach(Gear G in ViewBag.Gears)
{
_gears.Add(new GearViewModel(G, false));
}
ViewBag.GearsCheckList = _gears.ToList();
return View();
}
Now, this is the part I'm getting stuck at, is how to display and capture details in the CreateView.
I need assistance on how to design the Create form and how I will capture the info.
Firstly, view models should not contain data models when editing. You view models should be (add validation and display attributes as appropriate)
public class CarVM
{
public int? ID { get; set; }
public string Description { get; set; }
public List<GearVM> Gears { get; set; }
}
public class GearVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the GET method will be
public ActionResult Create()
{
var gears = db.Gears;
CarVM model = new CarVM
{
Gears = gears.Select(x => new GearVM
{
ID = x.gid,
Name = x.gname
}).ToList()
};
return View(model);
}
and the view will then be
#model CarVM
....
#using (Html.BeginForm())
{
..... // elements for editing ID and Description properties of CarVM
#for (int i = ; i < Model.Gears.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Gears[i].ID)
#Html.HiddenFor(m => m.Gears[i].Name) // include if your want to get this in the POST method as well
#Html.CheckboxFor(m => m.Gears[i].IsSelected)
#Html.LabelFor(m => m.Gears.IsSelected, Model.Gears[i].Name)
</div>
}
<input type="submit" .... />
}
Then in the POST method
public ActionResult Create(CarVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// To get the ID's of the selected gears
IEnumerable<int> selected = model.Gears.Where(x => x.IsSelected).Select(x => x.ID);
// Initialize your data models, save and redirect
}
I have a Product model created and would like to add relevant taxes for the product from a Tax model. A single product can have multiple taxes applied so I have created another model TaxApplied to store relations.
I have added a ListBox with MultiSelectList to the Create view for Product which shows the available taxes.
#Html.ListBox("AppliedTaxes", ViewBag.AppliedTaxes as MultiSelectList)
But I get the following error when I try to create a product with selected Taxes. How should I modify the view,model or controller such as to add the tax relations as well?
The ViewData item that has the key 'AppliedTaxes' is of type 'System.Collections.Generic.List`1[[StoreManager.Models.TaxApplied, StoreManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable<SelectListItem>'.
Models Described below
public class Product
{
public int ID { get; set; }
public string Name { get; set;
public float SalePrice { get; set; }
public List<TaxApplied> AppliedTaxes { get; set; }
}
public class Tax
{
public int ID { get; set; }
[DisplayName("Tax Code")]
public string TaxCode { get; set; }
[DisplayName("Tax Percent")]
public string Value { get; set; }
}
public class TaxApplied
{
public int ID { get; set; }
[ForeignKey("Product")]
public int ProductID { get; set; }
[ForeignKey("Tax")]
public int TaxID { get; set; }
public virtual Product Product { get; set; }
public virtual Tax Tax { get; set; }
}
Controller actions for Product create
//
// GET: /Product/Create
public ActionResult Create()
{
ViewBag.NavProduct = "active";
MultiSelectList taxes = new MultiSelectList(db.Taxes.ToList<Tax>(), "ID", "Name");
ViewBag.AppliedTaxes = taxes;
return View();
}
//
// POST: /Product/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
ViewBag.NavProduct = "active";
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(product);
}
You need to return a MultiSelectList from your generic list.
Here is an example of how to do it:
var appliedTaxes = new List<TaxApplied>();
appliedTaxes.Add(new TaxApplied { ID = 1, ProductID = 1 });
var items = appliedTaxes.Select(t => new SelectListItem { Text = t.ID.ToString(), Value = t.ProductID.ToString() }).ToList();
ViewBag.AppliedTaxes = new MultiSelectList(items, "Text", "Value");
Please note I am only using the Id's as the text and value for demonstration purposes.
Update
Change your post to this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
ViewBag.NavProduct = "active";
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
MultiSelectList taxes = new MultiSelectList(db.Taxes.ToList<Tax>(), "ID", "Name");
ViewBag.AppliedTaxes = taxes;
return View()
}
I currently have an application with a home page that shows a list of ten movies based on the date they were "created", or entered into the database. I would also like to show a list of the top ten movies based on the rating of each movie. Is there a way to pass in another model or alter my current ViewModel to do this? Here is the Index section of my Home Controller:
public ActionResult Index()
{
var model =
_db.Movies
.OrderByDescending(m => m.DateEntered)
.Take(10)
.Select(m => new MovieListViewModel
{
Id = m.Id,
Title = m.Title,
Genre = m.Genre,
ReleaseDate = m.ReleaseDate,
CountOfReviews = m.Reviews.Count()
});
return View(model);
}
And the ViewModel being passed in:
public class MovieListViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Genre { get; set; }
[Display(Name="Year Released")]
public DateTime ReleaseDate { get; set; }
public int CountOfReviews { get; set; }
}
Create a model that encompasses both lists:
public class MovieListViewModel
{
public List<MovieModel> Top10ByCreated { get; set; }
public List<MovieModel> Top10ByRating { get; set; }
}
public class MovieModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Genre { get; set; }
[Display(Name="Year Released")]
public DateTime ReleaseDate { get; set; }
public int CountOfReviews { get; set; }
}
Then in your controller:
var model = new MovieListViewModel();
model.Top10ByCreated = ...
model.Top10ByRating = ...
return View(model);
In your view, use MovieListViewModel as your model and use your two lists as needed.
Just create another viewmodel, with two properties for the two lists:
public class MovieIndexViewModel
{
public IEnumerable<MovieListViewModel> TopTenByDate { get; set; }
public IEnumerable<MovieListViewModel> TopTenByRating { get; set; }
}
In the controller you can create this viewmodel and pass the two lists:
public ActionResult Index()
{
var vm = new MovieIndexViewModel();
vm.TopTenByDate = ....;
vm.TomTenByRating = ...;
return View(vm );
}
You can compose a single view model that contains the two result sets. The controller then instantiates the parent type and populates the child collections as you see fit.
public sealed class HomeViewModel{
public MovieListViewModel TopTenByRating { get; set; }
public MovieListViewModel TopTenByDate { get; set; }
}
public ActionResult Index()
{
var model = new HomeViewModel();
model.TopTenByDate =
_db.Movies
.OrderByDescending(m => m.DateEntered)
.Take(10)
.Select(m => new MovieListViewModel
{
Id = m.Id,
Title = m.Title,
Genre = m.Genre,
ReleaseDate = m.ReleaseDate,
CountOfReviews = m.Reviews.Count()
});
model.TopTenByRating = // something else
return View(model);
}
For more complex scenarios, I prefer a dedicated composer that is responsible for setting up the view model (rather than having all the logic in the action method).
For a simple scenario like this, setting it up in the controller is the easiest way. If you find yourself needing to reuse the query logic, consider abstracting it to a helper class.
A different approach would be to create the main view and get it to render the results of 2 action methods returning 2 partial views each typed to one of the models.
You have different options you can use anyone of them
Use ViewModel
For view model you have to create a class and in this class you will define all models as properties of this class.Here are two classes.
public class EmployeeDetails
{
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Employee
{
public int Id { get; set; }
}
Here is viewmodel
public class ViewModel
{
public Employee emp { get; set; }
public EmployeeDetails empdet{ get; set; }
}
Now in Controller you will do like this
public ActionResult About()
{
ViewModel vm = new ViewModel();
vm.emp = new Employee();
vm.empdet = new EmployeeDetails();
return View(vm);
}
And in view you will receive it like this
#model ViewModel
Use Tuple
Tuple is used to store different types.You can store your required classes object in it and pass to view
In controller
Tuple<int, string> tuple = new Tuple<int, string>(1, "Hello world");
return View(tuple);
In view you will receive it like this
#model Tuple<int,string>
I feel a bit stupid.
I'm trying to get a hang of MVC 4, using boxing as a functional example.
I have WeightCategories in the database (Heavyweights, etc), and Boxers.
Seem simple. The relation is a boxer has a current weight category, but when I edit, I want it to be able to change it with a drop down.
I understand how to do it if it's a list I've made myself in the code, but I have problem understanding how to "load" the list from the WeightCategory table and show it in the view/model of the boxer.
So, here is my code for the WeightCategory item:
[Table("WeightCategories")]
public class WeightCategory
{
[Key]
public int WeightCategoryId { get; set; }
public WEIGHT_CATEGORIES WeightCategoryType { get; set; }
[Display(Name = "Weight Category Name")]
[Required]
[MinLength(5)]
public string Name { get; set; }
[Display(Name = "Weight Limit In Pounds")]
public int? WeightLimit { get; set; }
}
Here is the code for the boxer item
[Table("Boxers")]
public class Boxer
{
[Key]
public int BoxerId { get; set; }
public WeightCategory CurrentWeightCategory { get; set; }
[Required]
public string Name { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int Draws { get; set; }
public int Kayos { get; set; }
}
In the view, I'm really not sure how to tackle that, I'm pretty sure it's not automatic and I need to load the table somewhere in the controller maybe... I'm looking for best practice or something.
Something like that in the view at the end:
#Html.DropDownListFor(model => model.CurrentWeightCategory.WeightCategoryId,
new SelectList(Model.WeightCategories, "WeightCategoryId", "Name",
Model.WeightCategories.First().WeightCategoryId))
You could design a view model:
public class MyViewModel
{
public Boxer Boxer { get; set; }
public IEnumerable<SelectListItem> WeightCategories { get; set; }
}
and then have your controller action populate and pass this view model to the view:
public ActionResult Edit(int id)
{
var model = new MyViewModel();
using (var db = new SomeDataContext())
{
// Get the boxer you would like to edit from the database
model.Boxer = db.Boxers.Single(x => x.BoxerId == id);
// Here you are selecting all the available weight categroies
// from the database and projecting them to the IEnumerable<SelectListItem>
model.WeightCategories = db.WeightCategories.ToList().Select(x => new SelectListItem
{
Value = x.WeightCategoryId.ToString(),
Text = x.Name
})
}
return View(model);
}
and now your view becomes strongly typed to the view model:
#model MyViewModel
#Html.DropDownListFor(
x => model.Boxer.CurrentWeightCategory.WeightCategoryId,
Model.WeightCategories
)