Chaining multiple classes in a MVC4 view - asp.net-mvc

Let's say I have a model like this (simplified from the original):
public class Location
{
public int ID { get; set; }
public string BinNumber { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Description { get; set; }
public virtual Location Bin { get; set; }
}
public class LineOnPickList
{
public int ID { get; set; }
public virtual Item Item { get; set; }
}
The usual thing to do here on the LineOfPickList Create view would be to have a dropdownlist that listed all the Item Descriptions and put the selected item in the newly created LineOnPickList record when Create was clicked.
What I need to do however is show a dropdownlist of Location BinNumbers, yet still have the Item associated with that Location in the newly created LineOnPickList record.
How would that be done?

Define a view model for your drop down
public class ItemViewModel
{
public int ID { get; set; }
public string BinNumber { get; set; }
}
Then build the drop down list data in your controller action as follows
public class CreateLineOnPickListViewModel
{
public int ItemId { get; set; }
public IEnumerable<ItemViewModel> Items { get; set; }
}
public ActionResult Create()
{
var model = new CreateLineOnPickListViewModel();
model.Items = db.Items
.Select(i => new ItemViewModel { ID = i.ID, BinNumber = i.Bin.BinNumber });
return View(model);
}
Then in your view
#model CreateLineOnPickListViewModel
#Html.DropDownListFor(m => m.ItemId, new SelectList(Model.Items, "ID", "BinNumber"), "-")
Then your post action method in your controller would look like this
public ActionResult Create(CreateLineOnPickListViewModel model)
{
var item = new Item { ID = model.ItemID };
db.Items.Attach(item);
var lineOnPickList = new LineOnPickList { Item = item };
db.SaveChanges();
return View(model);
}

Related

ASP.NET MVC5 - Adding items to model virtual list

I have a model called club and each club has a virtual list property for the members of that club. I am lost as to how to add more members to that list and then save it to my database.
public class Club
{
[Key]
public int ClubID { get; set; }
public string ClubName { get; set; }
public string ClubDescription { get; set; }
//List of Members that are members of this Club
public virtual List<ClubMember> ClubMembers { get; set; }
}//end Club
This is the ClubMember model:
public class ClubMember
{
[Key]
public int MemberId { get; set; }
//First Name
[Display(Name = "First Name")]
public string MemberFName { get; set; }
//Last Name
[Required(ErrorMessage = "You must enter a Last Name")]
[Display(Name = "Last Name")]
public string MemberLName { get; set; }
[Display(Name = "Member Name")]
public string MemberName { get; set; }
public string MemberEmail { get; set; }
//Foreign Key for Club
public int ClubID { get; set; }
[ForeignKey("ClubID")]
public virtual Club Club { get; set; }
}
I am using a wrapper model to get a list of the selected ids for the members that the user wishes to add but I'm not sure if this is needed:
public class NewMemberList //Class used when adding new members to the members list of a club
{
public List<ClubMember> NewMembers { get; set; }
public List<int> SelectedIDs { get; set; }
}
This is what I currently have in my view for adding a member, it just displays a drop-down list with a list of members and a submit button
#model ultimateorganiser.Models.NewMemberList
#{
ViewBag.Title = "Add Members";
}
#using (Html.BeginForm(#Model.SelectedIDs))
{
#Html.AntiForgeryToken()
#Html.ListBoxFor(m => m.SelectedIDs, new MultiSelectList(Model.NewMembers, "UserId", "UserFName", Model.SelectedIDs))
<input type="submit" value="save" />
}
This is the controller method I have. It is not finished as I do not know how to handle the post part so that it gets the list of selected ids and adds all of the data for that member to the members list in the club:
[HttpGet]
public ActionResult AddMembers(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Club club = db.Clubs.Find(id);
if (club == null)
{
return HttpNotFound();
}
List<ClubMember> CurrentMembers = club.ClubMembers;
List<ClubMember> MembersList = new List<ClubMember>();
MembersList = db.ClubMembers.ToList();
ViewBag.CurrentMembersList = CurrentMembers;
return View(new NewMemberList() { NewMembers = MembersList });
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddMembers([Bind(Include = "SelectedIDs")] Club club)
{
if (ModelState.IsValid)
{
//Get selected members and add them to Members list for the club
return RedirectToAction("Index");
}
return View(club);
}
If you have questions or would like to see more of my code just ask.
Your view model should store the ClubId as well since you are adding the new members to a specific Club.
public class AddMembersToClub
{
public string Name { set;get;}
public int ClubId { set;get;}
public List<SelectListItem> Members { set;get;}
public int[] SelectedMembers { set;get;}
}
And in your GET action,
public ActionResult AddMembers(int id)
{
var club = db.Clubs.Find(id);
if (club == null)
{
return HttpNotFound();
}
var vm = new AddMembersToClub { ClubId=id , Name = club.ClubName };
//Here I am getting all the members, If you want a subset, update the LINQ query
vm.Members = db.ClubMembers
.Select(x=> new SelectListItem { Value = x.MemberId.ToString(),
Text=x.MemberFName }).ToList();
return View(vm);
}
and in your view, which is strongly typed to our AddMembersToClub view model. You need to keep the ClubId in a hidden form field as we need that in the HttpPost action.
#model AddMembersToClub
#using(Html.BeginForm())
{
<p>Adding members to #Model.Name</p>
#Html.HiddenFor(s=>s.ClubId)
#Html.ListBoxFor(s => s.SelectedMembers, Model.Members)
<input type="submit" />
}
And in your HttpPost action, Read the SelectedMembers property which is an int array storing the Id's of selected members and using the Id, get the Member entity and udpate the ClubId property.
[HttpPost]
public ActionResult AddMembers(AddMembersToClub model)
{
foreach(var item in model.SelectedMembers)
{
var member = db.ClubMembers.FirstOrDefault(s=>s.MemberId==item);
if(member!=null)
{
member.ClubId = model.ClubId;
}
db.SaveChanges();
}
return ReidrectToAction("Index");
}

MVC ASP.net Multiple Views trying to store tags

Model:
public class PublishedSongViewModel
{
public int Id { get; set; }
[Required(AllowEmptyStrings = false)]
public string SongName { get; set; }
//...
[Required]
public IEnumerable<string> Category { get; set; }
}
public class CategoryViewModel
{
public short Id { get; set; }
public string Title { get; set; }
public virtual ICollection<SongCategoryViewModel> SongCategory { get; set; }
}
public class SongCategoryViewModel
{
public int Id { get; set; }
[Required]
public int PublishedSongId { get; set; }
[Required]
public short CategoryId { get; set; }
}
View:
#model IList<PublishedSongViewModel>
#using (Html.BeginForm("PublishMusic", "Publish", FormMethod.Post, new { #enctype = "multipart/form-data", #id = "form-upload" }))
{
#Html.DropDownListFor(x => Model[i].Category, new SelectList(//Categories list here), new { #class = "form-control dl_Categories ", Multiple = "Multiple" })
}
Controller:
[HttpPost]
public ActionResult PublishMusic(IEnumerable<PublishedSongViewModel> songDetails)
{
if (songDetails != null)
{
IEnumerable<PublishedSongViewModel> savedSongs = (IEnumerable<PublishedSongViewModel>)(Session["UserSongs"]);
var lookupDetails = songDetails.ToDictionary(song => song.Id, song => song);
if (savedSongs != null)
{
foreach (var publishedSong in savedSongs)
{
var key = publishedSong.Id;
if (lookupDetails.ContainsKey(key))
{
var details = lookupDetails[key];
publishedSong.SongName = details.SongName;
}
db.SongCategories.Add(new SongCategoryViewModel { PublishedSongId = key, CategoryId = //categories id that user typed in on editorFor});
db.PublishedSongs.Add(publishedSong);
db.SaveChanges();
}
}
}
return View("Index");
}
I'v filled CategoryViewModel table up with data in my SQL.
1) How do I get the titles of CategoryViewModel and pass them in the SelectList(//Here) parameter in my viewmodel?
2) In the PublishMusic Action, how do I get the CategoryId for the SongCategoryViewModel from the one or more categories that the user selected from songDetails.Category?
I am not sure if I am on the right track with this. basically the categories are like tags, the user can select more than one. I'v also cut out unessential code to make easier to read.

Loop through values of object which is property of another object

I have 2 Objects, User and Menu, I want to loop into User.Menu to creat links like this:
#for (int i = 0; i < _Usuario.Menu.Count(); i++)
{
#Html.ActionLink( Convert.ToString(_Usuario.Menu.LinkName), Convert.ToString(_Usuario.Menu.ActionName), Convert.ToString(_Usuario.Menu.ControllerName))
}
But i dont have a counter for User.Menu, how could this be done ?
public class User
{
public Int64 Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime LoginTime { get; set; }
public Menu Menu { get; set; }
public List<string> Objects { get; set; }
public List<string> Controllers { get; set; }
//public List<string> Roles { get; set; }
public User()
{
Objects = new List<string>();
Controllers = new List<string>();
}
}
public class Menu
{
public List<string> LinkName { get; set; }
public List<string> ActionName { get; set; }
public List<string> ControllerName { get; set; }
public Menu()
{
LinkName = new List<string>();
ActionName = new List<string>();
ControllerName = new List<string>();
}
}
Your menu class doesn't make much sense as it implies that the link, action, and controller names are three separate sets of items. In reality there is a single set of menu items each consisting of a link, action, and controller. So this means you can rewrite Menu as:
public class Menu
{
public List<MenuItem> Items { get; set; }
public Menu()
{
Items = new List<MenuItem>();
}
}
public class MenuItem
{
public string LinkName { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
}
You'll have to rewrite your code that sets up menu, but that should be easy enough.
Then it is easy to loop through in your view.
#for (int i = 0; i < _Usuario.Menu.Items.Count(); i++)
{
#Html.ActionLink(_Usuario.Menu.Items[i].LinkName, _Usuario.Menu.Items[i].ActionName, _Usuario.Menu.Items[i].ControllerName)
}
An alternative way is to create a display template for the model and use #Html.DisplayFor(). This way you do not need to worry about the loop as it will do it for you. This is a good way to keep your razor view nice and clean.
Example
public class MenuItem
{
public string LinkName { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
}
Display template (menuitem.cshtml):
#model MenuItem
#Html.ActionLink(Model.LinkName, Model.ActionName, Model.ControllerName)
View:
#model IEnumerable<MenuItem>
#Html.DisplayForModel()

Multiple dropdowns on mvc view

How can I create multiple dropdowns in my view with values coming from my database? I can get one dropdown, but how do I add another one?
public class MyModel
{
public Category Category { get; set; }
public IEnumerable<SelectListItem> List { get; set; }
}
public ActionResult Page()
{
var query = model.MyModel.Select(c => new SelectListItem
{
Value = c.ModelDescription,
Text = c.ModelDescription
});
var model = new MyModel
{
List = query.AsEnumerable()
};
return View(model);
}
You can just add more properties to contain your other selectlistitems
public class MyModel
{
public Category Category { get; set; }
public IEnumerable<SelectListItem> List1 { get; set; }
public IEnumerable<SelectListItem> List2 { get; set; }
public IEnumerable<SelectListItem> List3 { get; set; }
}
And just run other queries you need to populate them. And then you can just use them in the view.

How to design a ViewModel for a todo list application?

I am creating a simple todo application which has two entities, tasks and categories.
To create a task, choosing a category is a must. For this, I figured I would need a ViewModel.
Here is the Task entity
public class Task
{
public int taskId { get; set; }
public int categoryId { get; set; }
public string taskName { get; set; }
public bool isCompleted { get; set; }
public DateTime creationDate { get; set; }
public DateTime completionDate { get; set; }
public string remarks { get; set; }
public string completionRemarks { get; set; }
}
Here is the Category entity
public class Category
{
public int categoryId { get; set; }
public string categoryName { get; set; }
}
How can I design a TaskCategoryViewModel so that I can bind the category in the CreateTask view?
Edit: I am using classic ADO.NET instead of Entity Framework or LINQ to SQL.
Kishor,
the best bet is have model that hods definition for your task and for category (all in one)
here is how everything hangs together.
where
IEnumerable<SelectListItem> Categories
is used for creating drop down list which is ready to use
<%= Html.DropDownListFor(model=>model.NewTask.categoryId, Model.Categories) %>
this will create you nice dropdown list
private IEnumerable<Category> GetCategories
{
get
{
List<Category> categories = new List<Category>
{
new Category() {categoryId = 1, categoryName = "test1"},
new Category() {categoryId = 2, categoryName = "category2"}
};
return categories;
}
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult CreateTask()
{
TaskModel taskModel = new TaskModel();
LoadCategoriesForModel(taskModel);
return View(taskModel);
}
private void LoadCategoriesForModel(TaskModel taskModel)
{
taskModel.Categories =
GetCategories.Select(
x =>
new SelectListItem()
{Text = x.categoryName, Value = x.categoryId.ToString(CultureInfo.InvariantCulture)});
}
public ActionResult CreateTask(TaskModel taskModel)
{
if (ModelState.IsValid)
{
// do your logic for saving
return RedirectToAction("Index");
}
else
{
LoadCategoriesForModel(taskModel);
return View(taskModel);
}
}
/// <summary>
/// your model for creation
/// </summary>
public class TaskModel
{
public Task NewTask { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
/// <summary>
/// Task
/// </summary>
public class Task
{
public int taskId { get; set; }
public int categoryId { get; set; }
public string taskName { get; set; }
public bool isCompleted { get; set; }
public DateTime creationDate { get; set; }
public DateTime completionDate { get; set; }
public string remarks { get; set; }
public string completionRemarks { get; set; }
}
/// <summary>
/// Category
/// </summary>
public class Category
{
public int categoryId { get; set; }
public string categoryName { get; set; }
}
In the TaskViewModel (I would prefer naming it CreateTaskViewModel) create property for categories select list
public IEnumerable<SelectListItem> CategoriesSelectList;
In controller, bind that property before returning view (note that this also should be done in post handler, when ModelState is invalid)
public ViewResult Create()
{
CreateTaskViewModel model = new CreateTaskViewModel();
model.CategoriesSelectList = _repository.AllCategories().Select(x=> new SelectListItem(){ Text = x.CategoryName, Value = x.CategoryId.ToString();}
}
And finally, in the view
Html.DropDownListFor(model => model.CategoryId, Model.CategoriesSelectList)
Edit:
In your code, _repository.AllCategories() should be replaced by your data access code, that returns object having type IEnumerable<Category>. It actually does not matter which data access technology you use. And do not forget to add the using System.Linq; statement to your controller file, if it's missing.

Resources