MVC Dropdown using ViewModels without Magic String - c#

I am trying to get a drop-down from a database to work. I did not get too far. I am trying to do it using viewModel and not use Magic String. I have a feeling I am not too far off. Could some please take a look and see what I am missing or doing wrong?
I am getting a compiling error at this line in the controller: viewModel.Courts = CourtList,
I am pretty sure it is wrong but I am running of ideas on how to do this.
Domain Models:
public class Parent
{
public int ParentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Court Court { get; set; }
//public IEnumerable<SelectListItem> Courts { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Court
{
public int CourtId { get; set; }
public string CourtName { get; set; }
public virtual ICollection<Parent> Parents { get; set; }
}
View Model:
public class ParentVM
{
public int ParentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//public int CourtId { get; set; }
//public string CourtName { get; set; }
public virtual Court Court { get; set; }
//public virtual IEnumerable<Court> CourtList { get; set; }
public IEnumerable<SelectListItem> Courts { get; set; }
public IList<ChildVM> Children { get; set; }
}
Controller:
// GET: Parents/Create
public ActionResult Create()
{
IEnumerable<SelectListItem> CourtList = db.Courts.ToList().Select(x => new SelectListItem
{
Value = x.CourtId.ToString(),
Text = x.CourtName,
});
//ViewBag.CourtList = new SelectList(db.Courts, "CourtId", "CourtName");
ParentVM viewModel = new ParentVM()
{
Children = new List<ChildVM>()
{
new ChildVM(){Name="", DOB="", Address=""},
//new ChildVM(){Name="2", DOB="2", Address="222"},
//new ChildVM(){Name="3", DOB="3", Address="3"},
},
viewModel.Courts = CourtList,
};
return View(viewModel);
}
// POST: Parents/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ParentVM viewModel)
{
if (ModelState.IsValid)
{
var parent = new Parent()
{
FirstName = viewModel.FirstName,
LastName = viewModel.LastName
};
db.Parents.Add(parent);
foreach (ChildVM item in viewModel.Children)
{
var child = new Child()
{
Name = item.Name,
DOB = item.DOB,
Address = item.Address
};
db.Childs.Add(child);
}
//Parent parent = new Parent();
//var employee = AutoMapper.Mapper.Map<Parent, ParentVM>(parent);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
View:
#Html.DropDownList("Courts", (IEnumerable<SelectListItem>)Model.Courts)

Your trying to bind you dropdownlist to property Courts which is IEnumerable<SelectListItem>. A <select> posts back a value type (the value of the selected item) which cannot be bound to a collection. You need an additional property to bind to (or you could bind to Court.CourtId, but the CourtName property of Court wont be bound on postback.
View model
public class ParentVM
{
public int ParentID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
public int? SelectedCourt { get; set; } // bind the dropdown to this
public SelectList CourtList { get; set; }
public IList<ChildVM> Children { get; set; }
}
Controller
public ActionResult Create()
{
ParentVM model = new ParentVM()
{
Children = new List<ChildVM>() .....,
CourtList = new SelectList(db.Courts, "CourtId", "CourtName"),
SelectCourt = // set a value here if you want a specific option to be selected
});
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ParentVM model)
{
if (ModelState.IsValid)
{
Court court = db.Courts.Find(model.SelectedCourt) // get the Court based on `SelectedCourt`
var parent = new Parent()
{
FirstName = model.FirstName,
LastName = model.LastName,
Court = court
};
db.Parents.Add(parent);
foreach (ChildVM item in viewModel.Children)
{
....
db.Childs.Add(child);
}
db.SaveChanges();
return RedirectToAction("Index");
}
model.CourtList = new SelectList(db.Courts, "CourtId", "CourtName"); // reassign select list
return View(model);
}
View
#Html.DropDownListFor(m => m.SelectedCourt, Model.CourtList, "--Please select--")
If the value of SelectedCourt matches the value of one of the options, it will be selected when the page is rendered, otherwise the first (label) option will be selected. When you post back, the value of SelectedCourt will be the value of the selected option

Related

Nested entity not being saved

I have the following method that saves an EmailTemplate. Based on the ID of a dropdown it populates the EmailAccount as the foreign entity property.
public ActionResult Edit([Bind(Include = "EmailAccountId, EmailTemplate")] EmailTemplateViewModel emailTemplateViewModel)
{
if (ModelState.IsValid)
{
if (emailTemplateViewModel.EmailAccountId > 0)
{
emailTemplateViewModel.EmailTemplate.EmailAccount = db.EmailAccounts.Find(emailTemplateViewModel.EmailAccountId);
}
db.Entry(emailTemplateViewModel.EmailTemplate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(emailTemplateViewModel);
}
Everything in the EmailTemplate saves fine apart from EmailAccount. In debugger I can see that the property is populated before db.SaveChanges() is called.
I am setting the entity state to EntityState.Modified but it's not picking up the foreign property.
I tried adding:
db.Entry(emailTemplateViewModel.EmailTemplate.EmailAccount).State = EntityState.Modified;
But this didn't work. How do I tell EntityFramework that it needs to save the nested entity?
Edit:
As requested
public class EmailTemplateViewModel
{
public List<EmailAccount> EmailAccounts { get; set; }
public EmailTemplate EmailTemplate { get; set; }
[Display(Name = "Email Account")]
public int EmailAccountId { get; set; }
public IEnumerable<SelectListItem> EmailAccountsList
{
get
{
var allEmails = EmailAccounts.Select(e => new SelectListItem { Value = e.ID.ToString(), Text = e.Email });
return DefaultEmailAccountList.Concat(allEmails);
}
}
public IEnumerable<SelectListItem> DefaultEmailAccountList
{
get
{
return Enumerable.Repeat(new SelectListItem
{
Value = "-1",
Text = "Select Email Account"
}, count: 1);
}
}
}
public class EmailTemplate
{
public int ID { get; set; }
[StringLength(50)]
[Index(IsUnique = true)]
public string Identifier { get; set; }
public int Interval { get; set; }
public string TitleTemplate { get; set; }
[DataType(DataType.MultilineText)]
public string BodyTemplate { get; set; }
public virtual EmailAccount EmailAccount { get; set; }
}
I was modifying before I attached so the change wasn't tracked.
This works
if (ModelState.IsValid)
{
db.Entry(emailTemplateViewModel.EmailTemplate).State = EntityState.Modified;
if (emailTemplateViewModel.EmailAccountId > 0)
{
emailTemplateViewModel.EmailTemplate.EmailAccount = db.EmailAccounts.Find(emailTemplateViewModel.EmailAccountId);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(emailTemplateViewModel);

C# Editing ViewModel

I have my ViewModel, and I have my controller to display from the ViewModel correctly, however I'm not sure how I would make the ViewModel editable, as to send the edited data back to the Model. I only want to edit the OrderArchiveViewModel, not the details
ViewModel;
public class OrderArchiveViewModel
{
public int OrderId { get; set; }
public System.DateTime OrderDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public decimal Total { get; set; }
public bool HasBeenShipped { get; set; }
public List<OrderDetailArchive> Details { get; set; }
}
public class OrderDetailArchive
{
public string Title { get; set; }
public string Colour { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
Controller;
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
List<T_shirt_Company_v3.ViewModels.OrderArchiveViewModel> list = (from o in new TshirtStoreDB().Orders
.OrderBy(o => o.OrderDate)
.Select(o => new OrderArchiveViewModel()
{
OrderId = o.OrderId,
Address = o.Address,
FirstName = o.FirstName,
LastName = o.LastName,
City = o.City,
OrderDate = o.OrderDate,
PostalCode = o.PostalCode,
Total = o.Total,
HasBeenShipped = o.HasBeenShipped,
Details = (from d in o.OrderDetails
select new OrderDetailArchive
{
Colour = d.Product.Colour,
Quantity = d.Quantity,
Title = d.Product.Title,
UnitPrice = d.UnitPrice
}).ToList()
}).ToList()select o).ToList();
ViewBag.ShippedMessage = list.Where(w => w.HasBeenShipped).Any() ? "Order has been shipped" : "Order is being processed";
return View(list);
}
I can suggest you to make an another two actions.
public ActionResult Edit(int id)
where you will get the Order by it's Id, map to ViewModel and pass it to the view where you will have textboxes for editing. Create another one Action for accepting post request with updated model:
[HttpPost]
public ActionResult Edit(OrderArchiveViewModel model)
When the the edit page is submitted you will have a updated model with the new data, then find your model in database by Id and update the properties.
Can u send the code of your View to get more clarification?
The already given answer could be done by redirect to a page for editing purpose.
Do you want to show the Editing fields above the Grid?
For this purpose, you can add New ViewModel like
public class NewViewModel
{
public OrderArchiveViewModel OrderArchiveViewModel { get; set; }
public List<OrderArchiveViewModel> OrderArchiveViewModelList { get; set; }
}
And you can send data using this NewViewModel to View containing both editable OrderArchiveViewModel depending on the Id and also the List of OrderArchiveViewModel by assigning the list present in Index() action.

Using the selected item from DropDownList in MVC

I'm trying to use the user selected item from the DropDownList to create a new entry in my Database table that is related/linked?(Not sure of correct wording for this) to the DropDownList item.
Here are my Models
public class TaskInstance
{
public int Id { get; set; }
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public Task TaskId { get; set; }
public virtual Task Task { get; set; }
}
public class TaskInstanceViewModel
{
[DataType(DataType.DateTime)]
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public virtual ICollection<Task> Task { get; set; }
}
public class Task
{
public Task()
{
TaskInstance = new HashSet<TaskInstance>();
}
public int Id { get; set;}
public string Name { get; set; }
public string Unit { get; set; }
public virtual ICollection<TaskInstance> TaskInstance { get; set; }
}
Controllers
public ActionResult Create()
{
var model = new TaskInstanceViewModel();
model.TaskList = db.Task.ToList().Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).ToList();
return View(model);
}
// POST: TaskInstances/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(TaskInstanceViewModel model)
{
if (ModelState.IsValid)
{
var taskinstance = new TaskInstance { DueDate = model.DueDate };
db.TaskInstance.Add(taskinstance);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
View - This is the only one I need to show I think, the others are just fields
#Html.DropDownListFor(x => Model.TaskList, (SelectList)Model.Task)
On the controller where it says var taskinstance = new TaskInstance { DueDate = model.DueDate }; Would be where i need to use the selected item from the User but I have no idea how to get it, i've looked through a lot of posts but most of them is just how to make the DropDownList in the first place but not how to use it(Being a link to another table) with a new database entry.
I'd also like to mention that I am still new to MVC so feel free to point out if im going about this the wrong way
Add a new property of type int to store the selected task from the dropdown. Remember view models are specific to the view.so keep only those properties you absolutely need in the view, in your view model.
public class TaskInstanceViewModel
{
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public int SelectedTask {set;get;} // new property
}
And in your view
#model TaskInstanceViewModel
#using(Html.BeginForm())
{
<label> How many</label>
#Html.TextBoxFor(s=>s.HowMany)
<label>Due date</label>
#Html.TextBoxFor(s=>s.DueDate)
<label>Task</label>
#Html.DropDownListFor(s => s.SelectedTask, Model.TaskList)
<input type="submit" />
}
And in your HttpPost action, you can use the SelectedTask property value which will have the Id of the task selected
[HttpPost]
public ActionResult Create(TaskInstanceViewModel model)
{
if (ModelState.IsValid)
{
var taskinstance = new TaskInstance { DueDate = model.DueDate ,
TaskId=model.SelectedTask };
db.TaskInstance.Add(taskinstance);
db.SaveChanges();
return RedirectToAction("Index");
}
model.TaskList = db.Task.ToList().Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).ToList();
return View(model);
}
It seems to me that you need you point to a property that represents the selected item in the dropdown. The dropdown items is IEnumerable<SelectListItem> why isn't the selected item a property of type SelectListItem?
Add a property to your view model:
public class TaskInstanceViewModel
{
[DataType(DataType.DateTime)]
public DateTime DueDate { get; set; }
public int HowMany { get; set; }
public IEnumerable<SelectListItem> TaskList { get; set; }
public virtual ICollection<Task> Task { get; set; }
//add this property:
public SelectListItem SelectedItem { get; set; }
}
And modify the view:
#Html.DropDownListFor(x => Model.TaskList, Model.SelectedItem )

MVC get child count in Index view

I used scaffolding to create the Index, Details, Create, Edit and Delete views and the controller. I have two view models (Parent / Child) relation. In my Index view I want to display the list of Teams as well as some information on the players (Parent / child). For example I want to display in the Index view the teams with the players count per team and last players that was modified. I am not sure where to begin.
Example:
(Team) Red -- (Last Modified) 01/02/2015 -- (Number Players) 10 and so on.
Team ViewModel
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public string NumberPlayers { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
}
Player ViewModel
public class PlayerVM
{
public int ID { get; set; }
public int TeamID { get; set; }
public string PlayerInfo { get; set; }
public DateTime? CreateDate { get; set; }
}
Other ViewModel
public class TeamViewModel
{
public List<Team> Teams{ get; set; }
}
Controller
public ActionResult Index()
{
TeamViewModelviewModel = new TeamViewModel();
viewModel.Teams= db.Teams.ToList();
return View(viewModel);
}
db.Products.ToList()?? I assume that is where you mean db.Teams.ToList()?
You are using viewmodels, so you should map the db data to your viewmodels first:
public ActionResult Index()
{
var teams = db
.Teams
.Include("Players") // Assuming your Team entity has a collection of Players
.SelectMany(t => new TeamVM {
ID = t.ID,
// etc..
})
.ToList();
return View(new TeamViewModel { Teams = teams });
}
model:
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
public int NumberPlayers {
get { return PlayerVM.Count(); }
}
}
Then in your view:
#model MyProject.Models.TeamViewModel
<table>
#foreach(var team in Model.Teams.ToList()) {
<tr>
<td>#team.Name</td> // Name
<td>#team.NumberPlayers</td> // Playercount
<td>#team.PlayerVM.Max(p => p.LastActivity).LastActivity</td> // Last edited
</tr>
}
</table>

ViewModel adding custom class to another class

I created this viewmodel:
public class PlayerViewModel
{
PlayerRepository repo = new PlayerRepository();
public Player Player { get; set; }
public int SelectedUserID { get; set; }
public SelectList Users { get; set; }
public PlayerViewModel()
{
Player = new Player();
}
public PlayerViewModel(int id)
{
Player = repo.Retrieve(id);
Users = new SelectList(repo.GetUsers());
SelectedUserID = 0;
}
}
this I have in view:
#Html.DropDownListFor(x => x.SelectedUserID, Model.Users)
#Html.ValidationMessageFor(x => x.SelectedUserID)
and this in controller:
[Authorize]
public ActionResult Upravit(int id)
{
var playerview = new PlayerViewModel(id);
return View(playerview);
}
[Authorize,HttpPost]
public ActionResult Upravit(int id, PlayerViewModel playerView)
{
if (ModelState.IsValid)
{
playerView.Player.User = usRepo.GetUserById(playerView.SelectedUserID);
repo.Save(playerView.Player);
return RedirectToAction("Podrobnosti", new { id = playerView.Player.PlayerID });
}
return View(playerView);
}
Now I have problem that " The field SelectedUserID must be a number." and I have in dropdownlist UserName. I modified this many times, I tried with Dictionary and other ways but everyway has some problem. So I want just ask for best way to add custom class User to class Player.
Player class:
public class Player
{
// pokud použijeme virtual a vlastností tak nám EF rozšíří o další možnosti jako lazy loading a další
[Key]
public int PlayerID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Surname { get; set; }
public string PhotoUrl { get; set; }
public string Post { get; set; }
public virtual Team Team { get; set; }
public virtual User User { get; set; }
// public int UserID { get; set; }
//public virtual ICollection<Article> Articles { get; set; }
// Here could be next things as number, ...
}
Thanks
Use this constructor instead:
http://msdn.microsoft.com/en-us/library/dd505286.aspx
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
Something like this:
Users = new SelectList(repo.GetUsers(),"UserID", "UserName");

Categories

Resources