I have been searching all over the internet about MVC many-to-many relationships (and have read every thing about it here too), but I can't seem to solve my problem.
I have a many-to-many relationship between a client and a standard service as seen below.
public class Client
{
public Client()
{
CustomServices = new HashSet<CustomService>();
StandardServices = new HashSet<StandardService>();
}
public int ClientID { get; set; }
public string Name { get; set; }
public Nullable<int> Users { get; set; }
public Nullable<bool> FH { get; set; }
public Nullable<bool> PH { get; set; }
public bool Hosted { get; set; }
public string ShortName { get; set; }
public Nullable<short> AttachmentLimit { get; set; }
public virtual ICollection<CustomService> CustomServices { get; set; }
public virtual ICollection<StandardService> StandardServices { get; set; }
And the standard service.
public class StandardService
{
public StandardService()
{
Clients = new HashSet<Client>();
}
public int StandardServiceID { get; set; }
public string StandardServiceName { get; set; }
public string LogOnAs { get; set; }
public int ServerID { get; set; }
public virtual Server Server { get; set; }
public virtual ICollection<Client> Clients { get; set; }
When creating a new client, I would like to be able to select from a list of check boxes for standard services that the client uses. I am able to get them to show up in the create view using the viewbag, but would like to be able to use a viewmodel.
I would also like to know how to get this to post with default model binding.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ClientID,Name,Users,FH,Hosted,PH,ShortName,AttachmentLimit")] Client client)
{
if (ModelState.IsValid)
{
db.Clients.Add(client);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(client);
}
Here is the create method for the Client Controller.
public ActionResult Create()
{
ViewBag.StandardServices = db.StandardServices.ToList();
return View();
}
Part of my create view is below (that does the check boxes).
<div class="form-group">
@Html.LabelFor(model => model.StandardServices, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@foreach (var StandardService in ViewBag.StandardServices)
{
<div class="checkbox">
<input class="checkbox" type="checkbox" name="StandardServiceID" id="StandardServiceID" value="@StandardService.StandardServiceID"/>
@StandardService.StandardServiceName<br>
</div>
}
</div>
</div>
So how could I go about doing this? I have tried many things, but don't want to go though listing them all. None worked suffice it to say. Any help at all would be appreciated. I don't need the full solution, just some advice on where to go. Thanks!
Based on a comment below; and viewing the link provided, I think I have part of the solution. I just need to work on the post now. Here is what I have for the view model.
public class CreateClientVM
{
public CreateClientVM()
{
ClientStandardServices = new List<StandardService>();
}
public int ClientID { get; set; }
public string Name { get; set; }
public Nullable<int> Users { get; set; }
public Nullable<bool> FH { get; set; }
public Nullable<bool> PH { get; set; }
public bool Hosted { get; set; }
public string ShortName { get; set; }
public Nullable<short> AttachmentLimit { get; set; }
public List<StandardService> ClientStandardServices { get; set; }
public List<StandardServiceVM> StandardServices { get; set; }
}
public class StandardServiceVM
{
public int StandardServiceID { get; set; }
public string StandardServiceName { get; set; }
public bool IsSelected { get; set; }
}
And below is my Client Controller create method.
public ActionResult Create()
{
//ViewBag.StandardServices = db.StandardServices.ToList();
CreateClientVM clientVM = new CreateClientVM();
var ListVM = new List<StandardServiceVM>();
var standardServices = db.StandardServices.ToList();
foreach(var s in standardServices)
{
var viewModel = new StandardServiceVM
{
StandardServiceID = s.StandardServiceID,
StandardServiceName = s.StandardServiceName,
IsSelected = false
};
ListVM.Add(viewModel);
}
clientVM.StandardServices = ListVM;
return View(clientVM);
}
And finally part of my create view.
<div class="form-group">
@Html.HiddenFor(m => m.ClientID)
@Html.LabelFor(m => m.StandardServices, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@for (int i = 0; i < Model.StandardServices.Count; i++)
{
<div class="checkbox">
@Html.HiddenFor(m => m.StandardServices[i].StandardServiceID)
@Html.CheckBoxFor(m => m.StandardServices[i].IsSelected)
@Html.LabelFor(m => m.StandardServices[i].IsSelected, Model.StandardServices[i].StandardServiceName)
</div>
}
</div>
</div>
Here is the html that gets rendered when viewing the page source. I think this looks right, but I'm not entirely sure.
<div class="form-group">
<input data-val="true" data-val-number="The field ClientID must be a number." data-val-required="The ClientID field is required." id="ClientID" name="ClientID" type="hidden" value="0" />
<label class="control-label col-md-2" for="StandardServices">StandardServices</label>
<div class="col-md-10">
<div class="checkbox">
<input data-val="true" data-val-number="The field StandardServiceID must be a number." data-val-required="The StandardServiceID field is required." id="StandardServices_0__StandardServiceID" name="StandardServices[0].StandardServiceID" type="hidden" value="1" />
<input data-val="true" data-val-required="The IsSelected field is required." id="StandardServices_0__IsSelected" name="StandardServices[0].IsSelected" type="checkbox" value="true" />
<input name="StandardServices[0].IsSelected" type="hidden" value="false" />
<label for="StandardServices_0__IsSelected">FHEmailService</label>
</div>
<div class="checkbox">
<input data-val="true" data-val-number="The field StandardServiceID must be a number." data-val-required="The StandardServiceID field is required." id="StandardServices_1__StandardServiceID" name="StandardServices[1].StandardServiceID" type="hidden" value="2" />
<input data-val="true" data-val-required="The IsSelected field is required." id="StandardServices_1__IsSelected" name="StandardServices[1].IsSelected" type="checkbox" value="true" />
<input name="StandardServices[1].IsSelected" type="hidden" value="false" />
<label for="StandardServices_1__IsSelected">FHScheduledReports</label>
</div>
</div>
</div>
Can anyone comment if this looks okay? I believe it does. Now I just need to figure out how to post it.