65.9K
CodeProject is changing. Read more.
Home

ASP.NET MVC Checklist Based on Templates with EF (All Core)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Sep 18, 2022

CPOL
viewsIcon

4922

A short example on how to create a hierarchical web checklist

Introduction

In this tip, you will see a short example to create a web checklist in ASP.NET with hierarchical structure (Checklist -> has Captions -> Have Entries).

Background

Based on a recent project, I wanted to post an example for those who need an inspiration for a similar task. The PJ is using EF Core, ASP.NET Core .NET 6.

The Model

    public class Checklist
    {
        [Key]
        public int Id { get; set; }
        public string Title { get; set; }
        public string Notes { get; set; }

        public virtual ChecklistType ChecklistType { get; set; }
        public virtual List<ChecklistCaption> ChecklistCaptions { get; set; }

    }

    public class ChecklistCaption
    {
        [Key]
        public int CaptionId { get; set; }
        public string CaptionName { get; set; }
        public string HtmlId { get; set; }
        public int SortOrder { get; set; }

        public List<ChecklistEntry> Entries { get; set; }
    }

    public class ChecklistEntry
    {
        [Key]
        public int EntryId { get; set; }
        public string Name { get; set; }
        public int SortOrder { get; set; }

        public string HtmlId { get; set; }

        public int ResponsibleUserId { get; set; }
        
        public string TaskName { get; set; }

        public int HideField { get; set; }
        
        public string Description { get; set; }
        
        public string HelpLink { get; set; }

        public int Radio { get; set; }
        public string RadioValue1Name { get; set; }
        public string RadioValue2Name { get; set; }
        public string RadioValue3Name { get; set; }

        public int RadioValue1 { get; set; }
        public int RadioValue2 { get; set; }
        public int RadioValue3 { get; set; }

        public string InputText { get; set; }

    }

You will see later that a negative Value of Radio will switch the control to an input field with InputText as Text-Field-Container.

I'm using code first and dependency injection in EF. The DB is a local SQLEXpress.

Steps Needed to Enable Entity Framework to be Usable in the Controller

Startup.cs (add service):

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllersWithViews();

     // this is the DbContext registration code
     services.AddDbContextFactory<ChecklistContext>(options =>
     options.UseSqlServer(
     Configuration.GetConnectionString("WebApiDatabase")));
}

DBContext (to define DBSet):

public class ChecklistContext : DbContext
{
   protected readonly DbContextOptions<ChecklistContext> _dbContextOptions;

   public ChecklistContext(DbContextOptions<ChecklistContext> dbContextOptions)
   : base(dbContextOptions)

   {
      _dbContextOptions = dbContextOptions;
   }

    public DbSet<Checklist> Checklists { get; set; }

}

Controller with the injection:

public class ChecklistController : Controller
{
        private readonly IDbContextFactory<ChecklistContext> _contextFactory;
        private readonly IConfiguration _configuration;

        public ChecklistController(IDbContextFactory<ChecklistContext> contextFactory, 
                                   IConfiguration configuration)
        {
            _contextFactory = contextFactory;
            _configuration = configuration;
        }

        public ActionResult Index()
        {
            return View("Index");
        }

        public ActionResult Edit(int? Id)
        {
            using (var context = _contextFactory.CreateDbContext())
            {
                var checklist = context.Checklists
                    .Where(x => x.Id == Id)
                    .Include(x => x.ChecklistCaptions).ThenInclude
                            (x => x.Entries) //Include fields
                    .FirstOrDefault();

                if (checklist == null)
                {
                    return NotFound();
                }

                return View(checklist);
            }
        }

View:

@model  ChecklistCore3.Models.Checklist

<script type="text/javascript">
    $(document).ready(function () {

    });

</script>

<h2>@Model.Title (Id: @Model.Id)</h2>

@using (Html.BeginForm("UpdateChecklist", "Checklist", 
        FormMethod.Post, new { @id = "myform" }))
{
    @Html.AntiForgeryToken()

    <fieldset>
        <table class="table checklist-table">
            @Html.HiddenFor(model => model.Id)

            <tr>
                <td>
                    <h3>
                        @Html.LabelFor(model => Model.Title)
                    </h3>
                </td>
                <td colspan="4">
                    <h3>
                        @Html.TextBoxFor(model => Model.Title, new { @class = "w800p" })
                    </h3>
                </td>
            </tr>

            @{
                for (int i = 0; i < Model.ChecklistCaptions.Count(); i++)
                {
                        <tr id="caption-@Model.ChecklistCaptions[i].HtmlId">
                            <td class="checklist-caption" colspan="6">
                                <span>@Model.ChecklistCaptions[i].CaptionName</span>
                                @Html.HiddenFor(model => 
                                Model.ChecklistCaptions[i].CaptionId)
                            </td>
                        </tr>

                    for (int j = 0; j < Model.ChecklistCaptions[i].Entries.Count(); j++)
                    {
                            <tr class="checklist-row responsible-
                             @((Model.ChecklistCaptions[i].Entries[j].
                             ResponsibleUserId > 0 && ViewBag.CurrUserId==
                             Model.ChecklistCaptions[i].Entries[j].
                             ResponsibleUserId).ToString())" 
                             id="@Model.ChecklistCaptions[i].Entries[j].HtmlId">
                                <td class="checklist-sortorder">
                                    @Html.DisplayTextFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].SortOrder)
                                </td>
                                <td class="checklist-taskname">
                                    @Html.HiddenFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].TaskName)
                                    @Html.HiddenFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].EntryId)
                                    @Html.HiddenFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].HideField, 
                                    new { @class = "HideField" })
                                    @Html.DisplayTextFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].TaskName)
                                </td>
                                <td class="checklist-description">
                                    @Html.HiddenFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].Description)
                                    @Html.DisplayTextFor(model => 
                                    Model.ChecklistCaptions[i].Entries[j].Description)
                                    @if (!string.IsNullOrEmpty
                                    (Model.ChecklistCaptions[i].Entries[j].HelpLink))
                                {
                                        <a href="@Model.ChecklistCaptions[i].
                                        Entries[j].HelpLink" target="_blank">->
                                        Link<span class="glyphicon 
                                        glyphicon-question-sign"></span></a>
                                }
                                </td>

                                <td class="checklist-checkbox">
                                    @if (Model.ChecklistCaptions[i].Entries[j].Radio >= 0)
                                {
                                        <div class="editor-field-radio">
                                            @if (!string.IsNullOrEmpty
                                            (Model.ChecklistCaptions[i].Entries[j].
                                             RadioValue1Name))
                                        {
                                                <label class="dp-check-block">
                                                 @Html.RadioButtonFor(model => 
                                                 Model.ChecklistCaptions[i].Entries[j].
                                                 Radio, Model.ChecklistCaptions[i].
                                                 Entries[j].RadioValue1)
                                                 <span class="big">
                                                 @Model.ChecklistCaptions[i].Entries[j].
                                                 RadioValue1Name</span></label>
                                        }
                                            @if (!string.IsNullOrEmpty
                                            (Model.ChecklistCaptions[i].Entries[j].
                                             RadioValue2Name))
                                        {
                                                <label class="dp-check-block">
                                                @Html.RadioButtonFor(model => 
                                                Model.ChecklistCaptions[i].Entries[j].
                                                Radio, Model.ChecklistCaptions[i].
                                                Entries[j].RadioValue2)
                                                <span class="big">
                                                @Model.ChecklistCaptions[i].
                                                Entries[j].RadioValue2Name</span></label>
                                        }
                                            @if (!string.IsNullOrEmpty
                                            (Model.ChecklistCaptions[i].Entries[j].
                                             RadioValue3Name))
                                        {
                                                <label class="dp-check-block">
                                                 @Html.RadioButtonFor(model => 
                                                 Model.ChecklistCaptions[i].Entries[j].
                                                 Radio, Model.ChecklistCaptions[i].
                                                 Entries[j].RadioValue3)
                                                 <span class="big">
                                                 @Model.ChecklistCaptions[i].
                                                 Entries[j].RadioValue3Name</span>
                                                 </label>
                                        }
                                        </div>
                                }
                                else
                                {
                                        <div class="editor-field-radio">
                                            @Html.HiddenFor(model => 
                                            Model.ChecklistCaptions[i].Entries[j].Radio)
                                            @Html.EditorFor(model => 
                                            Model.ChecklistCaptions[i].Entries[j].
                                            InputText)
                                        </div>
                                }

                                </td>
                            </tr>
                    }
                }
            }

            <tr>
                <td>
                    @Html.LabelFor(model => Model.Notes)
                </td>
                <td colspan="5">
                    @Html.TextAreaFor(model => Model.Notes, 
                    new { rows = "5", @class = "checklist-notes-textarea" })
                    @Html.ValidationMessageFor(model => Model.Notes)
                </td>
            </tr>

            <tr>
                <td colspan="5">
                    <div class="submit-div-checklist" title="Save checklist">
                        <p>
                            <input id="submit-btn" type="submit" value="Save" 
                             class="btn btn-success fright">
                        </p>
                    </div>
                </td>
            </tr>

        </table>
    </fieldset>
}

The View Iterates through captions and entries to create the checklist.

Model.ChecklistCaptions[i].Entries[j].TaskName

The Model Binding is able to recreate the class based on that structure when submitting the form.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateChecklist(Checklist checklist)
{
   //ToDo...
   return View("Edit",checklist);
}

History

  • 18th September, 2022: Initial version