Asp.net Core

Creating Dynamic User-Defined Dashboards using Asp.Net Core | Tutorial

In any Application, Dashboards provide a summary of the Application using different Graphical objects. Dashboards are important for a quick overview of the whole application that’s why in every application, Dashboards are considered as one of the most important features of any Application.

Have you ever thought as a user of an Application that there are some objects you don’t want in your dashboard because you don’t actually need those charts or graphs?

What is Dynamically Created or User-Defined Dashboards?

If you have used Google Analytics, you can choose what segment you want to view in your dashboard by Adding new Segments. something like this.

This is just a simple example. There are many more related examples available but In this Tutorial, we are going to Create these kinds of User-Defined Dashboards using Asp.Net Core.

This Tutorial is going to be little lengthy. So, take a cup of Coffee with you before you start.

What we’re going to Develop

Before start, we should know what we’ll get at the end. I would suggest you watch this short video of what we’re going to develop.

Now, I hope you have a clear idea what we’re going to develop.

Here’s what will be included in this Tutorial:

  1. Creating an Asp.Net Core Project
  2. Setting up UI Template in our Project
  3. Creating Database & Scaffolding in our Project
  4. Creating Dashboard Templates
  5. Creating Dashboard Elements

Let’s create an empty Asp.Net Core project

Creating an Asp.Net Core Project

I’m going to use dotnet cli for creating new project using the command below.

dotnet new empty

First of all, update your “Startup.cs” File with the below Code to ready your project for a Web Application.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseStaticFiles();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Dashboard}/{action=Index}/{id?}");
        });
    }
}

Now create new Folders as “Models”, “Views”“Controllers” & “wwwroot”  at the root directory of your project.

Setting up Template

First of all, you need to download the AdminLTE Template from GitHub.

So, Click Here & Clone or Download the complete AdminLTE Source, as you can see in below image.

Unzip the Downloaded file & you’ll get this.

We just need to Copy these two folders (“bower_components” & “dist”) from the above & Paste in the “wwwroot” folder of your Asp.Net Core Project we have created at the start of this Article.

Now, create a new file inside your Views Folder as _ViewStart.cshtml & put this Code.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Create a Folder as Shared & create 2 new Files inside this Folder as _Layout.cshtml & _MainMenu.cshtml

Here’s the Code for _Layout.cshtml

 <!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Dynamic Dashboards</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <link rel="stylesheet" href="~/bower_components/bootstrap/dist/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="~/bower_components/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="~/bower_components/Ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="~/dist/css/AdminLTE.min.css">
  <link rel="stylesheet" href="~/dist/css/skins/skin-blue.min.css">
  <!-- jQuery 3 -->
  <script src="~/bower_components/jquery/dist/jquery.min.js"></script>
  <!--Load the AJAX API-->
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Dropify/0.2.2/css/dropify.min.css">
  <!-- Google Font -->
  <link rel="stylesheet"
        href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
  <!-- Main Header -->
  <header class="main-header">
    <!-- Logo -->
    <a href="index2.html" class="logo">
      <!-- mini logo for sidebar mini 50x50 pixels -->
      <span class="logo-mini"><b>D</b>D</span>
      <!-- logo for regular state and mobile devices -->
      <span class="logo-lg">Dynamic<b>Dashboards</b></span>
    </a>
    <!-- Header Navbar -->
    <nav class="navbar navbar-static-top" role="navigation">
      <!-- Sidebar toggle button-->
      <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
        <span class="sr-only">Main Menu</span>
      </a>
      <!-- Navbar Right Menu -->
      <div class="navbar-custom-menu">
        <ul class="nav navbar-nav">
          <!-- User Account Menu -->
          <li class="dropdown user user-menu">
            <!-- Menu Toggle Button -->
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
              <!-- The user image in the navbar-->
              <img src="~/dist/img/user2-160x160.jpg" class="user-image" alt="User Image">
              <!-- hidden-xs hides the username on small devices so only the image appears. -->
              <span class="hidden-xs">Shehryar khan</span>
            </a>
            <ul class="dropdown-menu">
              <!-- The user image in the menu -->
              <li class="user-header">
                <img src="~/dist/img/user2-160x160.jpg" class="img-circle" alt="User Image">
                <p>
                  Shehryar khan - Dot Tutorials
                  <small>Member since Nov. 2012</small>
                </p>
              </li>
              <!-- Menu Body -->
              <li class="user-body">
                <div class="row">
                  <div class="col-xs-4 text-center">
                    <a href="#">Account</a>
                  </div>
                </div>
                <!-- /.row -->
              </li>
              <!-- Menu Footer-->
              <li class="user-footer">
                <div class="pull-left">
                  <a href="#" class="btn btn-default btn-flat">Profile</a>
                </div>
                <div class="pull-right">
                  <a href="#" class="btn btn-default btn-flat">Sign out</a>
                </div>
              </li>
            </ul>
          </li>
        </ul>
      </div>
    </nav>
  </header>
  <!-- Left side column. contains the logo and sidebar -->
  <aside class="main-sidebar">
    <!-- sidebar: style can be found in sidebar.less -->
    <section class="sidebar">
      <!-- Sidebar user panel (optional) -->
      <div class="user-panel">
        <div class="pull-left image">
          <img src="~/dist/img/user2-160x160.jpg" class="img-circle" alt="User Image">
        </div>
        <div class="pull-left info">
          <p>Shehryar Khan</p>
          <!-- Status -->
          <a href="#"><i class="fa fa-circle text-success"></i> Online</a>
        </div>
      </div>
      <!-- search form (Optional) -->
      <form action="#" method="get" class="sidebar-form">
        <div class="input-group">
          <input type="text" name="q" class="form-control" placeholder="Search...">
          <span class="input-group-btn">
              <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
              </button>
            </span>
        </div>
      </form>
      <!-- /.search form -->
      <!-- Sidebar Menu -->
      @Html.Partial("_MainMenu")
      <!-- /.sidebar-menu -->
    </section>
    <!-- /.sidebar -->
  </aside>
  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    @RenderBody()
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
  <!-- Main Footer -->
  <footer class="main-footer">
    <!-- Default to the left -->
    <strong>Copyright © 2019 <a href="https://dottutorials.net">Dot Tutorials</a>.</strong> All rights reserved.
  </footer>
</div>
<!-- ./wrapper -->
<!-- REQUIRED JS SCRIPTS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Dropify/0.2.2/js/dropify.min.js"></script>
<!-- Bootstrap 3.3.7 -->
<script src="~/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- AdminLTE App -->
<script src="~/dist/js/adminlte.min.js"></script>
</body>
</html>

We’ll Add Code for _MainMenu.cshtmlat the end of this Tutorial.

Creating Database

Here’s the Database, I have created for creating Dynamic Dashboards

I have also shared .sql File at GitHub. you can Download & Import this Database.

Getting Dashboard Name

You need to Create an Interface for Dashboard Name Input.

Create a new Folder inside Views Folder as Dashboard & create a new File as Index.cshtml inside this Dashboard folder & add this Code.

<style>
   #dashboard {
  align: center;  
  width: auto; 
  padding-left: 60px;
  padding-right: 60px;
  padding-top: 60px;
  padding-bottom: 60px;
}
#b {
    height: 500px;
}
</style>

<div align="center"   class="row">
    <div class="col-lg-12">
        <div id="b" class="white-box m-t-30 center-block">
            <form id="dashboard" >
                <h1 class="" >Welcome to Dashboard Wizard</h1>
                <div class="form-group">
                    <label for="exampleInputkwh">Enter Dashboard Name</label>
                    <div class="input-group">
                        <div class="input-group-addon"><i class="fa fa-dashboard"></i></div>
                        <input type="text" class="form-control" id="dashboard-name" name="dashboard-name"/> 
                    </div>
                    <br>
                    <div align="center">
                        <button type="button" class="btn custom-btn waves-effect waves-light" data-dismiss="modal" data-toggle="modal" onclick="DashboardTemplates()">Next</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

<script>
    function DashboardTemplates()
    {
        localStorage.setItem("dashboard_name", $("#dashboard-name").val());
        window.location = "/Dashboard/TemplateList/";
    }
</script>

It will create a view like this.

Please don’t run your project before complete setup.

Creating Dashboard Templates

For Creating Dashboards Dynamically, we need some Templates. I have already created some Templates, here’s the example.

TemplateList

For creating your own Templates, create a new Folder in your Views->Dashboard Folder with the name as Templates & Create Separate template in a Separate file as Partial View.

For Every Template, you need to add a record in Templates Table of your Database.

Here’s the List of Templates i have Created

TemplateList.cshtml is the UI for choosing your Template. In only shows all Templates as Partial Views in a list with radio buttons.

Here’s Code for TemplateList.cshtml

@{
    ViewBag.Title = "Dashboard's Template Selection";
}

<!-- Content Header (Page header) -->
<section class="content-header">
    <h1>
        Templates List
    </h1>
</section>

<!-- Main content -->
<section class="content container-fluid">
    <div class="row">
        <div class="col-xs-12">
            <div class="row">
                <br>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="1" type="radio" name="template" checked>
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t1.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 1</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="2" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t2.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 2</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="3" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t3.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 3</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="4" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t4.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 4</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="5" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t5.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 5</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="6" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t6.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 6</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="7" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t7.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 7</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="8" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t8.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 8</code> </p>
                    </div>
                </div>
            </div>
            <br><hr>
            <div align="right">
                <button type="btn" class="btn custom-btn waves-effect waves-light m-r-10" onclick="Next()">Next</button>
            </div>
        </div>
    </div>
</section>

<script>

    function Next(){

        $.ajax(
        {
            type: "POST",
            url: '@Url.Action("Createdashboard", "Dashboard")',
            data: {
                Name: localStorage.getItem("dashboard_name"),
                TemplateId: $('input[type=radio][name=template]:checked').attr('id')
            },
            error: function (result) {
                alert("error");
            },
            success: function (result) {

                if (result != "False") {
                    window.location = "/Dashboard/Dashboard/"+result;
                }
                else {
                    alert("There is a Problem!");
                }
            }
        });
        
    }
</script>

Creating Dashboard Elements

Elements are Charts & Graphs for our Dashboards. We’ll create a Separate Partial View for an Element & Create an Element List for choosing Elements.

Here’s the Element List created by me.

I have Added Created Some Charts using Google Charts Library with dummy Data.

So, create a new Folder as Elements inside your Dashboards Folder Views->Dashboard & create partial views for Elements.

Same as Templates, I have also created Elements as Separate Views & each Element needs to be Inserted as a record in Database like this.

For Every Element, you need to add a record.
Templates, I have created. a chart in each Element

Dashboards Logic

Create a Controller as DashboardController.cs & Add the Below Code for All the Interfaces we have created above.

using System.Collections.Generic;
using System.Linq;
using Dynamic_User_Defined_Dashboards.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace EnergyAxis.Controllers
{
    public class DashboardController : Controller
    {
        private Dashboard_TutorialContext db = new Dashboard_TutorialContext();
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult TemplateList(){
            return View("Templates/TemplateList");
        }

        public IActionResult Dashboard(int id)
        {
            DashboardsInfo dashboard = db.DashboardsInfo.Where(s => s.Id == id).FirstOrDefault();
            int elementsCount = (int)db.Templates.Where(s=>s.Id == dashboard.TemplateId).Select(s=>s.ElementsCount).FirstOrDefault();

            var linked_elements  = db.DashboardLinkedElements.Where(s=>s.DashboardId == id).ToList();
            for (int i = 1; i <= elementsCount; i++)
            {
                var element = linked_elements.Where(s=>s.Placement == i.ToString());
                if(element.Any()){
                    ViewData["Element"+i] = "../Elements/Element"+element.First().ElementId+".cshtml";
                }
                else{
                    ViewData["Element"+i] = "../Elements/Default.cshtml";
                }
            }

            ViewData["dashboardId"] = id;
            return View("Templates/Template"+dashboard.TemplateId);
        }

        public string Createdashboard(DashboardsInfo dashboard)
        {
            try
            {
                db.DashboardsInfo.Add(dashboard);
                db.SaveChanges();

                return dashboard.Id.ToString();
            }
            catch (System.Exception)
            {
                return "False";
            }
        }

     
        public IActionResult ElementList(int id){

            ViewData["dashboardId"] = id;
            return View("Elements/ElementList");
        }

        public string AddElement(DashboardLinkedElements element){

            var old = db.DashboardLinkedElements.Where(s=>s.DashboardId == element.DashboardId && s.Placement == element.Placement).ToList();
            foreach (var item in old)
            {
                db.DashboardLinkedElements.Remove(item);
            }
            db.SaveChanges();

            try
            {
                db.DashboardLinkedElements.Add(element);
                db.SaveChanges();
                return "True";
            }
            catch (System.Exception e)
            {
                return e.Message;
            }
        }

        public ActionResult GetDashboardsList()
        {
            return Ok(db.DashboardsInfo.ToList());
        }
    }
}

In the end, you need to create your Sidebar Menu from Database for displaying every Dashboard you create in your Menu.

So, here’s the Code for _MainMenu.cshtml you have created earlier insideViews->Shared

<ul class="sidebar-menu" data-widget="tree">
<li class="header">HEADER</li>
<!-- Optionally, you can add icons to the links -->
<li><a href="/Dashboard/Index"><i class="fa fa-dashboard"></i> <span>Create New</span></a></li>
<li class="treeview">
    <a href="#"><i class="fa fa-dashboard"></i> <span>Dashboards</span>
    <span class="pull-right-container">
        <i class="fa fa-angle-left pull-right"></i>
        </span>
    </a>
    <ul class="treeview-menu" id="menu">
    </ul>
</li>
</ul>

<script>

    $(function() {

        $.ajax(
        {
            type: "POST",
            url: '@Url.Action("GetDashboardsList", "Dashboard")',
            error: function (result) {
                alert(result);
            },
            success: function (result) {
                var menu = null;
                $.each( result, function( key, value ) {
                    menu += '<li><a href="/Dashboard/Dashboard/'+value.id+'">'+value.name+'</a></li>';
                });
                $("#menu").html(menu);
            }
        });
});
</script>

Run your Application now. I hope everything will work Fine.

I have also shared the complete project on Github. You just required .Net Core SDK 2.2 for running this Project.

Here’re some more articles, you might be interested:

– TOP OPEN SOURCE ASP.NET CORE CONTENT MANAGEMENT SYSTEM (CMS)

– USING NOSQL DATABASE WITH DOTNET CORE EXAMPLE

– GENERATE QR CODE USING ASP.NET CORE

Author

I'm passionate about learning new technologies as well as mentoring and helping others get started with their programming career. This blog is my way of giving back to the Community.

5 Comments

  1. Hello, excellent dashboard.
    Do you have it but to SQLServer?

    Thanks!!

  2. Weldone, My name is Opeyemi from Lagos Nigeria. Please i would love you to mentor me. Am a student of computer science with different project giving us in school. this is my whatsapp number and imo +2348152328349. my gmail is [email protected]. please how can i contact you

Write A Comment