MVCCheckBoxList catering for a 1 to Many relationship - c#

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
}

Related

Displaying data from multiple models on one view (images and text)

Hi everyone so I am trying to create an application using asp.net mvc with a code first database that allows the users to be able to create a blog post with as many images as they wish.I have the data stored in the database but I I am currently trying to have the the head, body and images displaying in the display view this is what I would like it to look like : http://imgur.com/a/IR19r but I am not sure how to accomplish this. I am able to display the head and body but cannot get the images from the image table here is the database diagram: http://imgur.com/a/lvwti
Currently this is the error I get when i add this to the view #Html.DisplayFor(modelItem => item.Images)
An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: An error occurred while executing the command definition. See the inner exception for details.
Model
public partial class PostModel
{
public PostModel()
{
Images = new List<ImageModel>();
}
[Key]
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Required(ErrorMessage = "Heading is Required")]
[Display(Name = "Heading")]
public string Heading { get; set; }
[Required(ErrorMessage = "Body is Required")]
[DataType(DataType.MultilineText)]
[Display(Name = "Body")]
public string Body { get; set; }
public virtual ICollection<ImageModel> Images { get; set; }
public IEnumerable<HttpPostedFileBase> File { get; set; }
}
public class ImageModel
{
[Key]
public int ID { get; set; }
public string Path { get; set; }
public virtual PostModel Post { get; set; }
public string DisplayName { get; set; }
}
public class ImageVM
{
public int? ID { get; set; }
public string Path { get; set; }
public string DisplayName { get; set; }
public bool IsDeleted { get; set; }
}
public partial class PostVM
{
public PostVM()
{
Images = new List<ImageVM>();
}
public int? ID { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
public IEnumerable<HttpPostedFileBase> Files { get; set; }
public List<ImageVM> Images { get; set; }
}
DbContext
public class EFDbContext : DbContext
{
public DbSet<PostModel> Posts { get; set; }
public DbSet<PostVM> PostVMs { get; set; }
public DbSet<ImageModel> Images { get; set; }
public DbSet<ImageVM> ImageVMs { get; set; }
}
Controller
public ViewResult Display()
{
return View(repository.Posts)
}
View
#model IEnumerable<Crud.Models.PostModel>
#{
ViewBag.Title = "Index";
}
#foreach (var item in Model)
{
<div>
#Html.DisplayFor(modelItem => item.Heading)
</div>
<div>
#Html.DisplayFor(modelItem => item.Body)
</div>
<div>
#Html.DisplayFor(modelItem => item.Images)
#*<img class="img-thumbnail" width="150" height="150" src="/Img/#item.Images" />*#
</div>
}
Here is alternative controller I tried but am not using as I got this error when i tried let Images = i.Path and wasn't really sure if this was meant to be how it was done
Cannot implicity convert typeCrud 'string' to 'System.Collections.Generic.List Crud.Models.ImageVm '
public ViewResult Display()
{
IEnumerable<PostVM> model = null;
model = (from p in db.Posts
join i in db.Images on p.ID equals i.Post
select new PostVM
{
ID = p.ID,
Heading = p.Heading,
Body = p.Body,
Images = i.Path
});
return View(model);
}
item.Images is a collection. So loop through that and display the images.
<div>
#foreach(var image in item.Images)
{
<img src="#image.Path" />
}
</div>
You need to make changes to the src property depending on what value you store in the Path property of image.
You can correct your other action method like this
public ViewResult Display()
{
var posts = db.Posts.Select(d => new PostVM()
{
ID = d.ID ,
Heading = d.Heading,
Body = d.Body,
Images = d.Images.Select(i => new ImageVM() { Path = i.Path,
DisplayName = i.DisplayName }
).ToList()
}).ToList();
return View(posts);
}
Now since you are returning a list of PostVM, make sure your Display view is strongly typed to that.
#model List<PostVM>
<h1>Posts</h1>
#foreach(var p in Model)
{
<h3>#p.Heading</h3>
<p>#p.Body</p>
#foreach(var image in item.Images)
{
<img src="#image.Path" />
}
}
Also, there is no point in keeping the view model classes on your db context. Keep only your entity models. View models are only for the use of UI layer.
public class EFDbContext : DbContext
{
public DbSet<PostModel> Posts { get; set; }
public DbSet<ImageModel> Images { get; set; }
}

ASP MVC Model issue

I have a model:
public class MenuItem
{
public int Id { get; set; }
....
[StringLength(50)]
public string PageTemplate { get; set; }
public int MenuId { get; set; }
public Menu Menu { get; set; }
}
Notice the PageTemplate. I need to somehow create a property that list out files from a folder. This property should then be a selectlist in the view, and then saved in the PageTemplate.
What would the proper way of doing this be?
In the model add:
public IEnumerable<SelectListItem> Templates { get; set; }
In the action method populate your data to the property:
public ActionResult Create()
{
return View(new MenuItem
{
Templates = _yourFilesList.
.Select(f => new SelectListItem { Text = f.Title })
});
}
[HttpPost]
public ActionResult Create(MenuItem item)
{
var userTemplate=item.PageTemplate; // user's selected item is there
}
Finally in the view add this
#Html.DropDownListFor(x => x.PageTemplate , Model.Templates)

Entity Framework & ASP.NET MVC more complex models

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" })

Using two models in one controller?

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>

MVC 4 Simple Populate DropDown from database model

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
)

Categories