Step-by-Step Creation of a Dashboard from a Gadget Template
Onesait platform has a very powerful and flexible dashboard system for data representation. In this tutorial, you are going to create a dashboard that allows you to see the high degree of customization, the power of connection and interaction with the data, and the speed of development.
To do this, once inside the onesait platform, we will go to the Display > Dashboards Management menu, where we can create a new dashboard.
Dashboard creation and initial configuration
To create a dashboard, we will go to its creation page and fill in the basic fields of identification, description, image, whether it is public or not, category and subcategory. The dashboard has a series of properties that will allow us to configure its operation. These are:
- Initial Dashboard Style (type):
It allows you to choose an initial dashboard style that configures not only its style and behavior, but also a series of JavaScript and CSS libraries that can be loaded initially in the dashboard. These initial styles can be pre-created in the general management of the console by Administrator users to have a library of dashboard types where you can choose the style and initial libraries that the dashboard will load. This management (initial dashboard styles) has been recently added to the Onesait functionalities:
- Global Styles, Libraries and Scripts:
Allows you to load here a number of JS and CSS files to be used later in the dashboard. These are designed to load third-party plugins or UX styles according to each project, or JavaScript controllers with the logic of certain template gadgets to structure, organize and optimize the code in the dashboards. These files and settings are just what can be previously saved in dashboard style management, which we have talked about in the initial Dashboard style.
In this dashboard that you are going to create, you will use a number of JS libraries and styles to use in several components such as the horizontal slider, the progress bars or the Datatable table. These files are the libraries of these plugins and their styles:
<meta http-equiv="Cache-control" content="public"> <!-- common styles --> <link href="https://lab.onesaitplatform.com/web/enel/assets/app/css/style.bundle.css" rel="stylesheet" type="text/css" /> <link href="https://lab.onesaitplatform.com/web/enel/assets/vendors/base/vendors.bundle.css" rel="stylesheet" type="text/css" /> <link href="https://lab.onesaitplatform.com/web/enel/assets/app/css/style.override.css" rel="stylesheet" type="text/css" /> <link href="https://lab.onesaitplatform.com/web/enel/assets/vendors/base/soho.css" rel="stylesheet" type="text/css" /> <link href="https://lab.onesaitplatform.com/web/enel/assets/app/css/jquery-confirm.min.css" rel="stylesheet" type="text/css" /> <link href="https://lab.onesaitplatform.com/web/enel/assets/vendors/datatables/datatables.bundle.css" rel="stylesheet" type="text/css" /> <!-- custom gadget template styles --> <link href="https://lab.onesaitplatform.com/web/enel/assets/app/css/gadgetTemplate.css" rel="stylesheet" type="text/css" />
Next, you will also load the additional JavaScript files for the plugins you will use:
<!-- common scripts --> <script src="https://lab.onesaitplatform.com/web/enel/assets/vendors/base/vendors.bundle.js"></script> <script src="https://lab.onesaitplatform.com/web/enel/assets/app/js/scripts.bundle.js"></script> <script src="https://lab.onesaitplatform.com/web/enel/assets/app/js/jquery-confirm.min.js"></script> <script src="https://lab.onesaitplatform.com/web/enel/assets/vendors/datatables/datatables.bundle.js"></script> <script src="https://cdn.jsdelivr.net/gh/wmh/jquery-scrollbox@1.4.2/jquery.scrollbox.min.js"></script> <!-- Gadget template controller --> <script src="https://lab.onesaitplatform.com/web/enel/assets/app/js/ENEL_Controller.js"></script> <!-- additional angular directives --> <script defer="defer" src="https://unpkg.com/angularjs-gauge@2.2.0/dist/angularjs-gauge.min.js"></script>
Finally, still in this section of the dashboard configuration, you will enter a code to dynamically load AngularJS directives. This is very useful, since a part of the Dashboads engine is implemented on Angular:
<script> // DYNAMIC LOADING OF ANGULAR DIRECTIVES window.addEventListener('DOMContentLoaded', function() { window.setTimeout( function(){ angular.element(document.getElementsByTagName('body')[0]).injector().loadNewModules(['angularjs-gauge']) },0 ); }); </script>
Dashboard Construction
When building a dashboard, you must bear in mind several things: the dashboard's structure or layout and the components that you will include, which will be described in the dashboard's WIREFRAME. The other key element when building a dashboard is data. Data will give life to the dashboard's visual components and you must know what data they are, either DATASOURCES or API, and what filters or behaviors they have with each other, which will be described in the dashboard's DATA COMMUNICATION.
Wireframe
Just like in the Web design, a layout or an initial design is made to have a clear structure of the page. A dashboard is exactly the same, you need to know what information is going to be represented and how to define the spaces, the dashboard structure and the components that you'll need. Let's see these examples in the dashboard that you are going to build:
With this information, you can get an idea of the structure by knowing the proportions that the zones will have, then, through the gadget wireframe, you get an idea of what visualization elements you are going to place in each zone to represent the information, and finally, to help the programmers and the dashboard's programming, you can also see the plugins that you will be using in each of the zones. Whether you are going to build a dashboard of a single gadget template with all these programmed elements, or you are going to use N gadgets distributed throughout the different zones, these functional design elements are always a reference, allowing to save a lot of time and above all maintainability of the same dashboard.
The resulting dashboard will look like this, although you still have to see how the data will be connected, and how gadgets interact with each other to filter the information:
Data Communication
Once you have a clear idea about the dashboard structure (layout) and the visual elements (gadgets) that you are going to use to represent the information, you of course need the Data to be represented. There are two ways to bring data from the platform to the dashboard: API (REST) and Datasources. Usually, due to power, data transformation and query optimization, we will use Datasources (advanced SQL query via webSocket with configuration, transformation, paging and filtering capabilities).
Just as it is important to be clear about the visual structure, it is equally or more important to know what data you are going to need, how is this data structured, and most importantly, how they are orchestrated with each other. That is to say, usually you are going to load one or several gadgets with their datasources, and you are also going to load multiple datasources in other gadgets templates. The important thing is also to know how the gadgets filter each other, giving life and power to the Dashboard. Let's see below the data schemas of this Dashboard:
With this information we quickly see the correlation between zones, gadgets and data sources (DS Datasources) directly with the first image. The second and third images show something that happens a lot in dashboards and that happens in the case of multi-datasources: Loading processes of multiple datasources depending on the interaction with its components. In this example we see that we have 4 datasources, 1 for each zone, and two loading processes, 1 the initial one, which is called "All", which initially loads the Datasource of Zone 1 DS_Plant and triggers the load when the data arrives. of the 2nd Datasource DS_Centrales, which in turn triggers the loading of the 3rd and 4th Datasources DS_Production and DS_Aeros, since they depend on the Central, and the central, in turn, depends on the Plant. The code that allows this fantastic interaction is:
With this information, you can easily see the correlation between zones, gadgets and data sources (DS Datasources) directly with the first image. The second and third images show something that happens a lot in dashboards and that, in the case of multi-datasources, can happen: Multiple data loading processes depending on the interaction with its components. In this example, you can see that you have four datasources, one for each zone, and two loading processes, one the initial one, which is called "All" and initially loads the Datasource of Zone 1 DS_Planta and is triggered with the arrival of data from of the second Datasource, DS_Centrales, that in turn triggers the load of the third and fourth Datasources DS_Production and DS_Aeros, since they depend on the Plant, and the plant in turn depends on the Plant. The code that allows this excellent interaction is:
// load datasources filtering by central console.log('LOAD DATASOURCE DS_CENTRALES: ' + filter); // vm.getDataFromDataSource('DATASOURCE_ID', CALLBACK FUNCTION TO TX DATA, [{FILTERS}...{}]) vm.getDataFromDataSource('DS_UNITS_CENTRALES', vm.callbackCentral, [{field:'CENTRAL',op:'=',exp: filter}]);
In this way, in the code of a gadget template, you can load a Datasource programmatically. Remember that if you only need one, you can link it to the gadget directly from its edition; but in this case you will want to load several ones. The best way to do this is as follows: Create a function with all possible cases "All", "Central", ... as a load dispatcher, depending on the filters sent either between gadgets, or between the value sending function, vm.sendValue() or the filter sending function vm.sendFilter() filters. They will receive these filter values and act by launching the synchronized load of all or some of the necessary datasources.
From there, using the callBack functions that the datasources loading system possesses, you will process the information or simply update it directly in the display gadgets.
/** Datasources loader */ vm.loadDatasources = function(category, filter){ //... // FILTER DISPATCHER switch(category){ case "month": // load datasources filter by month and year console.log('LOAD DATASOURCES BY MONTH'); break; case "central": console.log('LOAD DATASOURCES BY CENTRAL: ' + filter); vm.getDataFromDataSource('DS_CENTRALES', vm.DS_CentralesCallBack, [{field:'CENTRAL',op:'=',exp: filter}]); vm.getDataFromDataSource('DS_AEROS', vm.DS_AerosCallBack, [{field:'CENTRAL',op:'=',exp: filter}]); break; case "graph": // load datasources of production graph with selected interval, adding month and year, and central too, console.log('LOAD DATASOURCES BY MONTH,CENTRAL AND GRAPH...'); break; case "aero": // load aero datasources filter by month and year and central console.log('LOAD DATASOURCES BY MONTH,CENTRAL AND AERO...'); break; case "all": // initial loading of datasources console.log('LOAD DATASOURCES ALL , INIT CASE'); vm.getDataFromDataSource('DS_PLANTA', vm.DS_PlantaCallBack, [{field:'PLANTA',op:'=',exp:filter}]); break; } //... // See one CallBack function. /** Datasource DS_Planta callback Function */ vm.DS_PlantaCallBack = function( data ){ // set data vm.indicators = JSON.parse(data); // variable directamente enlaza con la visualización console.log('Indicators data: ' + vm.indicators); // load the related datasources, AEROS and PRODUCTION depends on central data. // for AEROS, we filter by PLANT and from central selection we can filter both production and aeros. vm.getDataFromDataSource('DS_AEROS', vm.DS_AerosCallBack ,[{field:'CENTRAL',op:'=',exp: vm.centrals[0].CENTRAL}]); // set current Central vm.currentCentral = vm.centrals[0].CENTRAL; vm.currentCentralName = vm.centrals[0].NOMBRE_CENTRAL; // for production we mount initially last 24hours for the first Central Available let now = moment().toISOString(); let last24h = moment(now).subtract(24, 'hour').toISOString(); vm.getDataFromDataSource('DS_PRODUCCION', vm.DS_ProduccionCallBack, [{field:'CENTRAL',op:'=',exp:vm.centrals[0].CENTRAL},{field: 'FECHA', op: '>=' , exp: "TIMESTAMP('"+ last24h +"')"},{field: 'FECHA', op: '<=' , exp: "TIMESTAMP('"+ now +"')"}]); // do animations setTimeout(function(){ ENEL_Controller.animateCounters('','countIndicators'); ENEL_Controller.animateProgressBars('','progressIndicators'); }, 50) };
Finally, program each of the cases. That is why it's important having the connection and filtering schemes of gadgets; and with that you can get all the interaction between components and the optimized loading of their data.