(ASP.NET Core 5 Ajax) Admin Panel with Content on the Right

This project is about creating an Admin Panel that would be suitable for a reporting project or an EBook that shows a table of links on the left and the corresponding content on the right side. The content is obtained by an Ajax call to an ASP.NET Core application. Source code for all the three files has been given and explained below.

Categories | About |     |  

Parveen,

First create an asp.net core application based on an empty template. Add a folder called pages, and then right click it to add a razor page called index. We will be working with the three files - startup.cs, index.cshtml and index.cshtml.cs.

The project to be built

Following is the screenshot of the project that we have to build

You can also see a tick-mark that will be positioned to indicate the currently active report.

Configure the Startup.cs file

Two changes are required in the startup.cs file - first one is for adding the services for razor pages, and the second is for adding razor pages as the end-points:

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Hosting;

namespace Ajax
{

  public class Startup
  {

    public void ConfigureServices(IServiceCollection services)
    {

      // add support for razor pages 
      services.AddRazorPages();

    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

      if (env.IsDevelopment())
      {

        app.UseDeveloperExceptionPage();

      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {

        // add razor pages as the end-points 
        endpoints.MapRazorPages();

      });

    }

  }

}

The Index.cshtml razor markup

Write this code in the IndexModel cshtml file. The first three lines are the usual boilerplate code. Then CSS styles have been written for a grid layout and rudimentary styling.

@page

@model Ajax.Pages.IndexModel

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<style type="text/css">

  #wrapper {
    display: grid;
    grid-template-columns: minmax(100px, auto) 1fr;
  }

    #wrapper > div {
      border-right: 1px solid silver;
      padding: 4px;
    }

  a[data-rep] {
    margin: 4px;
    display: block;
    padding: 4px;
    background-color: #efefef;
    text-decoration:none;
  }

  h1{text-align:center;}

</style>

<!-- MARKUP STARTS HERE -->

<div id="wrapper">

  <div>

    <h1>Available Reports</h1>

    <!-- hard coded for ease of tutorial -->

    <a data-rep="r1">Report of Customers</a>

    <a data-rep="r2">Report of Receivables</a>

    <a data-rep="r3">Report of Inventory</a>

    <a data-rep="r4">Report of Cancelled Bills</a>

  </div>

  <div>

    <span id="pw" style="color:red;"></span>

    <div id="report"></div>

  </div>

</div>

<script type="text/javascript">

  (function () {

    // check mark for placing adjacent to  
    // the currently active report 
    var tick = document.createElement("span")

    tick.style.color = "Red";

    tick.innerHTML = " ✔";

    // select all anchors 
    // with attribute data-rep 
    document.querySelectorAll("a[data-rep]").forEach(rep => {

      // prevent default anchor href 
      rep.setAttribute("href", "javascript:");

      rep.addEventListener("click", async function (event) {

        pw.innerHTML = "please wait...";

        // send an ajax GET request to 
        // get the report from the server 
        // the function on the server should be 
        // JsonResult OnGetRep(String id) 
        fetch("/?handler=rep&id=" + rep.dataset.rep,
          {
            method: "GET"
          }
        )
          // if any exceptions - log them 
          .catch(err => console.log("network error: " + err))

          .then(response => {

            // read json from the response stream 
            response.json().then(data => {

            // show the report in the 
            // div with id=report 
              report.innerHTML = data;

            });

            // hide the please wait message 
            pw.innerHTML = "";

            // append a tick mark 
            // to the anchor 
            this.append(tick);

          });

      })

    });

    report.innerHTML = "<p>click a link on the left</p>";

  })();

</script>

Here is the explanation of the above code:

<div id="wrapper">
This div is the main container that is styled as a grid. The left pane holds the links for the various reports or chapters that could be available to a user.
<h1>Available Reports</h1>
This pane contains hard-coded links for the available reports. In a real project these links could be constructed on the basis of a database query.
<div id="report"></div>
This DIV will receive the html of the report from the server. An ajax call will bring the html, and then the innerHTML of this DIV will be set equal to that.
var tick = document.createElement("span")
This is a DOM span element that contains a tick-mark unicode character. This span will be appended to the active anchor to serve as an indicator for the report being shown currently.
document.querySelectorAll("a[data-rep]").forEach
This is a selector that runs a foreach loop on each anchor with an attibute "data-rep", and then binds each anchor to a click event that sends an AJAX fetch request to the server. The AJAX request sends the ID of the report to be obtained as a parameter.
report.innerHTML = data;
The html data for the report is set as the innerHTML of the right side div, with id="report" - that we have explained above.

The Index.cshtml.cs backing class

Following is the code for the index.cshtml.cs file:

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.RazorPages;

using System;

using System.Text;

using System.Threading.Tasks;

namespace Ajax.Pages
{

  public class IndexModel : PageModel
  {

    public async Task<JsonResult> OnGetRep(string id)
    {

      // artificial delay 
      await Task.Delay(500);

      // get report from database 
      String report = GetReportForID(id);

      return new JsonResult(report);

    }

    private String GetReportForID(String id)
    {

      StringBuilder ret = new StringBuilder();

      // name of the report 
      switch (id)
      {

      case "r1":
        {

          ret.Append($"<h1>Report of Customers</h1>");

        }

        break;

      case "r2":
        {

          ret.Append($"<h1>Report of Receivables</h1>");

        }

        break;

      case "r3":
        {

          ret.Append($"<h1>Report of Inventory</h1>");

        }

        break;

      case "r4":
        {

          ret.Append($"<h1>Report of Cancelled Bills</h1>");

        }

        break;

      }

      // simulating some database query 
      // any random garbage being sent 
      ret.Append("<h4>Just a random garbage</h4>");

      ret.Append("<p>");

      Random rand = new Random();

      for (int x = 0; x < 200; x++)
      {

        ret.Append((char)rand.Next('a', 'z'));

        ret.Append(" ");

      }

      ret.Append("</p>");

      return ret.ToString();

    }

  }

}

Explanation of the above code is as follows -

OnGetRep(string id)
This C# function receives the Ajax request coming from the client side. It receives a String id as a parameter. Then it sends that parameter to a function GetReportForID for obtaining the html of the relevant report. The return type of this function must be JsonResult and its name must be prefixed with "OnGet".
private String GetReportForID(String id)
This is a placeholder function that should query a database and obtain html corresponding the string id parameter. Since this is just a tutorial, I have created a random garbage of 200 characters as the report.

Run the project to see that the data is indeed sent as an Ajax GET call. It shows a "please wait" message and then displays the same input string converted to uppercase.

Video Explanation

Please watch, write your comments and discussion on the following youtube video:


This Blog Post/Article "(ASP.NET Core 5 Ajax) Admin Panel with Content on the Right" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Comments and Discussion