Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET / ASP.NET-Core

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

5.00/5 (2 votes)
18 Sep 2022CPOL 4.9K  
A short example on how to create a hierarchical web checklist
Hierarchical checklist in ASP.NET using Entity Framework and Razor models. Stripped of all unnecessary elements like CSS, JavaScript. You may need to check if you save the entries when clicking on the form and not together with the complete form but the exercise is to show a hierarchical structure.

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

C#
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):

C#
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):

C#
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:

C#
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:

JavaScript
@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.

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

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

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

History

  • 18th September, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)