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

  • Wow awesome this was so incredibly easy to get working in my project thanks you rock! I see how one would save a list that is sortable from this demo http://trykendoui.telerik.com/@Alexander/Uciv/2 but how would you save the position of the panels in this demo? thanks again so cool

    • burkeholland

      Awesome! Glad you liked it.

      The way I did it in the past was to have a table which had a username, column and order for each module that a user had, so it might be…

      username moduleid row column
      burkeholland 1 2 2

      When the data is retrieved on user login, I used that data to dynamically compose both the layout and the selected modules.

      • Hi thanks for the response I have this bit now but the less obvious is how to order and mark the html for kendo to firstly list this order and second know it has changed to then save it. Any demos you could point to would be great.

  • Malcolm Sheppard

    @Burke: Thx, impressive. However, works in Chrome but not IE. As a possible alternative to SharePoint, this is a must.

    Vertical height of container cuts off content (see snippet).

  • Vesselin Obreshkov

    Great article. Been spending a lot of time learning Angular lately. Glad to see you guys using it more with Kendo (and making sure everything works well together).

  • Connor James Leech

    is all of this free? When do I run into having to pay for kendo?

    These seem to be the relevant github repos: https://github.com/telerik/kendo-ui-core , https://github.com/kendo-labs/angular-kendo/

    • burkeholland

      Everything used here is free. You will end up having to pay for Kendo UI if you use a Grid, Scheduler, TreeView, Upload, Editor or any Data Visualization.

      There is a relevant chart on the Core repo README if you scroll down…

      https://github.com/telerik/kendo-ui-core

  • Hello Burke,
    We have just add your great tutorial to twittstrap resource list
    https://twittstrap.com/customizable-dashboards-angular-kendo-ui-bootstrap/
    Best Regards

    The Twittstrap Team
    http://twittstrap.com | The number one attraction for all your bootstrap needs

  • Paul

    This looks great, I am having some trouble getting the resizing to work 100% on the codepen example. For instance, if I pick a widget and drop it back in the same spot, the pie chart seems to dissapear. The bar chart will dissapear and shrink, is this a browser compatibility problem? I have tested it in the latest Chrome and IE and the sample behaves the same in both.

    Thanks,

    http://codepen.io/burkeholland/full/hfbEa

  • Pingback: Real-time Web Applications with Kendo UI and Firebase -Telerik Developer Network()

  • karan

    This is great but i am facing one issue while using this ,Am new in this and i can’t understand how to save it.So that the next time he/she opens the dashboard find the reports at the same location they have allocated .
    Hope i will get some help.
    Thanks

  • Nazaf

    How do I make the widgets resizable ?

  • droidalmatter

    Great post! I have an additional requirement that I am grappling with. I need to persist the layout rearrangement that the user has made. I have the “non-default” location for a panel being stored in local storage. I write this out in the dropped function that is called by the change handler. Can you point me in a direction that would allow me to relocate the panels to the users stored locations?