Search code examples
asp.net-mvcdata-bindingasp.net-mvc-2viewmodel

How do I create a selection list using checkboxes in ASP.NET MVC?


I have a database table that records what publications a user is allowed to access. The table is very simple - it simply stores user ID/publication ID pairs:

CREATE TABLE UserPublication (UserId INTEGER, PublicationID INTEGER)

The presence of a record for a given user & publication means that the user has access; absence of a record implies no access.

I want to present my admin users with a simple screen that allows them to configure which publications a user can access. I would like to show one checkbox for each of the possible publications, and check the ones that the user can currently access. The admin user can then check or un-check any number of publications and submit the form.

There are various publication types, and I want to group the similarly-typed publications together - so I do need control over how the publications are presented (I don't want to just have a flat list).

My view model obviously needs to have a list of all the publications (since I need to display them all regardless of the current selection), and I also need a list of the publications that the user currently has access to. (I'm not sure whether I'd be better off with a single list where each item includes the publication ID and a yes/no field?).

But that's as far as I've got. I've really no idea how to go about binding this to some checkboxes. Where do I start?


Solution

  • The Linq to SQL model for your problem looks something like this:

    alt text

    First, we need some helper objects in our data model:

    namespace SelectProject.Models
    {
        public class UserPublicationSelector
        {
            public int UserPublicationID { get; set; }
            public int UserID { get; set; }
            public int PublicationID { get; set; }
            public string PublicationName { get; set; }
            public bool IsSelected { get; set; }
        }
    
        public class UserPublicationSelectViewModel
        {
            public User User { get; set; }
            public IQueryable Selections { get; set; }
        }
    }
    

    Now let's create a repository that looks like this:

    public class Repository
    {
        DataContext dc = new DataContext();
    
        public User GetUser(int userID)
        {
            return dc.Users.FirstOrDefault(u => u.UserID == userID);
        }
    
        public IQueryable GetUserPublications(int userID)
        {
            return from p in dc.Publications
                   join up in dc.UserPublications on p.PublicationID equals up.PublicationID
                   where up.UserID == userID
                   orderby p.PublicationName
                   select p;
        }
        public IQueryable GetUserPublicationSelectors(int userID)
        {
            return from p in dc.Publications
                   join up in dc.UserPublications on p.PublicationID equals up.PublicationID into selected
                   from s in selected.DefaultIfEmpty()
                   orderby p.PublicationName
                   select new UserPublicationSelector
                   {
                       UserPublicationID = (int?)s.UserPublicationID ?? 0,
                       UserID = userID,
                       PublicationID = p.PublicationID,
                       PublicationName = p.PublicationName,
                       IsSelected = s.UserID != null
                   };
        }
    
        public void UpdateUserPublications(UserPublicationSelector[] selections)
        {
            // Insert records for new selections...
            foreach (UserPublicationSelector selection in selections.Where(s => s.IsSelected == true))
            {
                // ...where records do not yet exist in database.
                if (selection.UserPublicationID == 0)
                {
                    UserPublication up = new UserPublication
                    {
                        UserID = selection.UserID,
                        PublicationID = selection.PublicationID,
                    };
                    dc.UserPublications.InsertOnSubmit(up);
                }
            }
            // Delete records for unselected items...
            foreach (UserPublicationSelector selection in selections.Where(s => s.IsSelected == false))
            {
                // ...where record exists in database.
                if (selection.UserPublicationID > 0)
                {
                    UserPublication up = dc.UserPublications.FirstOrDefault(s => s.UserPublicationID == selection.UserPublicationID);
                    if (up.UserID == selection.UserID && up.PublicationID == selection.PublicationID)
                        dc.UserPublications.DeleteOnSubmit(up);
                }
            }
            // Update the database
            dc.SubmitChanges();
        }
    }
    

    And a controller that looks like this:

    public class PublicationController : Controller
    {
        Repository repository = new Repository();
    
        public ActionResult Index(int id)
        {
            User user = repository.GetUser(id);
            var publications = repository.GetUserPublications(id);
            ViewData["UserName"] = user.UserName;
            ViewData["UserID"] = user.UserID;
            return View("Index", publications);
        }
    
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Select(int id)
        {
            var viewModel = new UserPublicationSelectViewModel()
            {
                User = repository.GetUser(id),
                Selections = repository.GetUserPublicationSelectors(id)
            };
            return View("Select", viewModel);
        }
    
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Select(int userID, UserPublicationSelector[] selections)
        {
            repository.UpdateUserPublications(selections);
            return RedirectToAction("Index", new { id = userID });
        }
    }
    

    The Index view looks like this:

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Publication>>" %>
    <%@ Import Namespace="SelectProject.Models" %>
    
    <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
        List of Selected Publications for User
    </asp:Content>
    
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    
        <h2>Publications for <%= ViewData["UserName"] %></h2>
    
            <table id="MyTable" style="width: 100%">
                <thead>
                    <tr>
                        <th>
                            Publication Name
                        </th>
                    </tr>
                </thead>
    
                <tbody>
                    <%  int i = 0;
                        foreach (Publication item in Model)
                        { %>
    
                        <tr id="row<%= i.ToString() %>">
                            <td>
                                <%= Html.Encode(item.PublicationName)%>
                            </td>
                        </tr>
    
                        <% i++;
                        } %>
                </tbody>
            </table>
            <p>
                <%= Html.ActionLink("Edit Selections", "Select", new { id = ViewData["UserID"] })%>
            </p> 
    
    </asp:Content>
    

    And the Select view looks like this:

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<UserPublicationSelectViewModel>" %>
    <%@ Import Namespace="SelectProject.Models" %>
    
    <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
        Select Publications
    </asp:Content>
    
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    
        <h2>Select Publications for <%= Model.User.UserName %></h2>
    
        <% using (Html.BeginForm())
           { %>
    
            <table id="MyTable" style="width: 100%">
                <thead>
                    <tr>
                        <th style="width: 50px; text-align:center">
                            <input type="checkbox" id="SelectAll" />
                        </th>
                        <th>
                            Publication Name
                        </th>
                    </tr>
                </thead>
    
                <tbody>
                    <%  int i = 0;
                        foreach (UserPublicationSelector item in Model.Selections)
                        { %>
    
                        <tr id="row<%= i.ToString() %>">
                            <td align="center" style="padding: 0 0 0 0">
                                <%= Html.CheckBox("selections[" + i.ToString() + "].IsSelected", item.IsSelected)%>
                                <%= Html.Hidden("selections[" + i.ToString() + "].UserPublicationID", item.UserPublicationID)%>
                                <%= Html.Hidden("selections[" + i.ToString() + "].UserID", Model.User.UserID)%>
                                <%= Html.Hidden("selections[" + i.ToString() + "].PublicationID", item.PublicationID)%>
                            </td>
                            <td>
                                <%= Html.Encode(item.PublicationName)%>
                            </td>
                        </tr>
    
                        <% i++;
                        } %>
                </tbody>
            </table>
            <p>
                <%= Html.Hidden("userID", Model.User.UserID) %>
                <input type="submit" value="save" />
            </p> 
    
        <% } // End Form %>
    
        <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    
        <script type="text/javascript">
            // Select All Checkboxes
            $(document).ready(function() {
                $('#SelectAll').click(function() {
                    var newValue = this.checked;
                    $('input:checkbox').not('input:hidden').each(function() {
                        this.checked = newValue;
                    });
                });
            });
        </script>
    
    </asp:Content>
    

    Here are some screen shots.

    alt text

    alt text

    The checkbox in the upper left hand corner is a Select All/Select None checkbox.