Customizable Dashboards With Angular Kendo UI and Bootstrap

Dashboards are a great way to see information. Columns are composed of “widgets” which are more or less agnostic of each other. The user can then rearrange these widgets to make the page look how they want. This is a concept that has been around in business intelligence for a long time, and something that has made it’s way into the mainstream with services like Facebook which offer a modicum of user customization.

Enabling users to tweak how their page looks with drag and drop is incredibly powerful. Nothing translates with a higher fidelity than WYSIWYG. As developers, we’re used to “flying blind” as we code, imagining what the end product will look like and checking on our progress every so often. For users, this is simply unacceptable.

What You Can’t See Is A PITA

Users need to be able to see how their pages will look. When building dashboards, we could give users a separate page with checkboxes and then ask them in what column and where they would like a specific widget to appear, but that’s a difficult environment to work in since – well – checkboxes and numbers are not how the page appears, so why would you use that interface to specify the appearance of the dashboard?

Kendo UI Core Sortables and Bootstrap make customizable dashboards a LOT of fun to build and ridiculously easy to get working. Additionally, dashboards tend to get markup heavy so we’ll use Kendo UIs seamless Angular integration to make use of Directives and Controllers to tame the mess. I decided to redo my “CatStat” dashboard from last year complete with drag and drop customization, collapsible panels and a new Bootstrap look and feel.

See the Pen hfbEa by Burke Holland (@burkeholland) on CodePen.

This was a LOT of fun. Lets build it together.

Grid Layout

To start with, lets prototype how the dashboard is going to look. Using a Bootstrap grid, it’s quite easy to specify a 2 column dashboard that stacks columns on top of each other whenever we get below tablet size (1024 x 768). That’s right, our dashboard is going to be fully responsive and therefore perfectly suited for mobile out-of-the box.

We could choose all sorts of layouts and even offer more than one layout, but I’m going to go with just the two column layout for now.

<body ng-controller="DashboardController">
  
  <div class="container" id="dashboard">
  <div class="row">
    <!-- side column -->
    <div class="col-sm-4" id="side">
    <div class="widget"></div>
    <div class="widget"></div>
    <div class="widget"></div>
    </div>
    <!-- main column -->
    <div class="col-sm-8" id="main">
    <div class="widget"></div>
    <div class="widget"></div>
    <div class="widget"></div>
    </div>
  </div>
  </div>
  
</body>

See the Pen vqHcf by Burke Holland (@burkeholland) on CodePen.

Note that I used the col-sm class for Bootstrap. This specifies that we will have a two column layout until the size of the browser gets below 1024 x 768 (tablet size), at which time the columns will stack.

Note: I added some temporary CSS classes to help us visualize what the dashboard is going to look like.

The next thing to do is to replace those placeholders with Kendo UI Core PanelBars.

Kendo UI Core PanelBars

PanelBars are generally used to have multiple sections that expand and collapse when you click on the header. It’s also commonly referred to as an “Accordian”. We can use a PanelBar and just have one panel which will translate into a single widget that we can expand and collapse.

<div class="container" id="dashboard">
  <div class="row">
    
    <!-- side column -->
    <div class="col-sm-4" id="side">
    
      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 1</span>
          <div>Some Content</div>
        </li>
        </ul>
      </div>
      
      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 2</span>
         <div>Some Content</div>
        </li>
        </ul>
      </div>
      
      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 3</span>
          <div>Some Content</div>
        </li>
        </ul>  
      </div>
    
    <!-- end side column -->
    </div>

    <!-- main column -->
    <div class="col-sm-8" id="main">

      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 4</span>
          <div>Some Content</div>
        </li>
        </ul>  
      </div>
      
      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 5</span>
          <div>Some Content</div>
        </li>
        </ul>  
      </div>
      
      <div class="widget">
        <ul kendo-panel-bar>
        <li class="k-state-active">
          <span class="k-link k-state-selected">Widget 6</span>
          <div>Some Content</div>
        </li>
        </ul>  
      </div>
    
    <!-- end main column -->
    </div>

  <!-- end row -->
  </div>

<!-- end container -->
</div>

See the Pen Iwpeh by Burke Holland (@burkeholland) on CodePen.

Thanks to Angular Kendo UI, we now have 2 columns of collapsible “widgets” which stack on top of each other for mobile. We also have a healthy bowl of “div soup”. Since Angular gives the power back to the HTML, it also makes for much more verbose markup. I would highly recommend using comments to delineate where one major section starts and ends. The human brain is not so good at whitespace.

The next step is to make these nice collapsible widgets customizable. For this, we’ll be using the Kendo UI Core Sortable.

Kendo UI Core Sortables

The sortables widget allows you to move the contents of any HTML element around and even between containers. The options that we will be using are…

  • connectWith – specifies the id of the container that can receive items from this container
  • filter – restricts which components inside the specified sortable are, in fact, sortable. By default this is the first child element of the sortable.
  • hint – specifies what HTML will show while the item is being dragged (can be a string or a function returning a string/element)
  • placeholder – denotes what HTML will show in the spot where the item is going to be dropped (can be a string or a function returning a string/element)

The first two items are just string settings. The second two we will specify as functions on the scope. Both the #side and #main containers will be turned into sortables.

<!-- side column -->
<div kendo-sortable class="col-sm-4" id="side" 
     k-connect-with="'#main'"
     k-filter="'.widget'"
     k-hint="hint"
     k-placeholder="placeholder">
  
  <!-- content omitted for brevity -->

<!-- end side column -->
</div>

<!-- main column -->
<div kendo-sortable class="col-sm-8" id="main" 
     k-connect-with="'#side'"
     k-filter="'.widget'"
     k-hint="hint"
     k-placeholder="placeholder">
  
  <!-- content omitted for brevity -->

<!-- end main column -->
</div>

The placeholder and hint functions will go on the controller. They essentially clone the element we are dragging and add some special classes to it to change the appearance slightly.

(function($) {
    
  var app = angular.module("app", ["kendo.directives"]);
  
  // controller for the body
  app.controller("DashboardController", function ($scope) {
    
    // function to create the placeholder for a sortable
    $scope.placeholder = function(element) {
      return element.clone().addClass("placeholder");
    };

    // function to create a hint while dragging a sortable element
    $scope.hint = function(element) {
      return element.clone().addClass("hint")
        .height(element.height())
        .width(element.width());
    };
    
  });
  
}(jQuery));

I stole this placeholder class from the Kendo UI Demos for the Sortable Panels because they are simply gorgeous. What was that quote about great artists again?

.widget.placeholder {
  opacity: 0.4;
  border: 1px dashed #a6a6a6;
}

And just like that we can drag and drop widgets in their current column, and even move them between columns.

See the Pen cDaoI by Burke Holland (@burkeholland) on CodePen.

Now lets add some useful content here. By useful, I mean “charts showing data about cats”.

Kendo UI DataViz Charts

Kendo UI DataViz is a powerful charting and data vizualization library that is part of Kendo UI Professional. By default, it draws charts to SVG (with a VML fallback), but will also draw to canvas if you prefer. Angular Kendo UI covers not just Kendo UI Core, but Professional as well, and that includes DataViz. We can add some charts to this page and put a little data in our controller to simulate a robust and complex set of data about cats.

See the Pen IJAzb by Burke Holland (@burkeholland) on CodePen.


Now we have a completely customizable dashboard! That was actually rather easy considering what the thing actually does. Thanks to Bootstrap and Kendo UI, it looks amazing. Thanks to Angular Kendo UI, it required very little JavaScript.

HOWEVER… There’s always a however. This site works great until you drag a chart from a larger area (main) to a smaller area (side). The chart then extends outside the bounds of it’s container. Yuck.

Fortunately, this is super easy to fix.

Receiving Events And Resizing

Whenever a drag operation happens in a sortable, it raises the change event. We are concerned with whenever a sortable receives and item. When that happens, we want to resize the chart inside of the widget that was dropped. All this sounds complicated, but Kendo UI makes this simple by giving you:

That means we can add one simple method to the scope that will handle resizing the charts when they are dropped.

$scope.dropped = function(e) {
  if (e.action === 'receive') {
    // call the resize API on the whole widget
    kendo.resize(e.item);
  }
};

Then we just need a k-on-change(kendoEvent) for each kendo-sortable div. Listening to the “receive” action tells us when the container received an item from another container. This is an important distinction in this case because there is no point in resizing any widgets that were dropped from the side column back into the side column.

Notice that you can call the kendo.resize method on any element. You don’t have to have a Kendo UI Widget instance. The API will automatically traverse that element, find any Kendo UI widgets and resize them for you.

See the Pen HFodc by Burke Holland (@burkeholland) on CodePen.

Lastly, we also need the charts to resize if the window is resized by the user. We accomplish this using the same resize API during the window resize event. We can also add a header with a Kendo UI Mobile NavBar at the top for a nice clean header.

A Few Extra Notes

There are a few other things of note here…

Kendo UI And CSS

Kendo UI does not make assumptions about CSS for you outside of it’s own widgets. If it did, you would constantly be fighting the CSS to get it out of your project. The net effect of this is that you need to be able to add in some of the required CSS to style your pages. In the case of the NavBar, I put it in a header with a fixed position and changed the background and font colors to match the rest of the app, while moving the content down 60px to account for the fixed position header.

#header {
  position: fixed;
  top: 0;
  z-index: 1024;
  width: 100%;
}

#dashboard {
  margin-top: 60px;
}

.km-navbar {
  background: #428bca;
  color: white;
}

Empty Sortable Lists

If you were to drag all of the items out of one list and into another, you would not be able to drag them back into the empty list since it would then have a height of 0. To prevent this, give your sortables a min-height CSS property so that you can always drag back into the empty list.

#side, #main {
  min-height: 100px;
}

You Don’t Need No SharePoint

Rich dashboards used to be relegated to expensive BI tools and SharePoint installations, but with Bootstrap, Angular and Kendo UI, you can build dashboards that are far more interactive, and arguably much better looking. So the next time someone asks for a dashboard, you can tell them, “No Problem!” and then go have some fun with Kendo UI Core Sortables and blow some minds.

Comments