ROUTE-TO-PA Datalet Developer Guide

Revision

Date

Authors

Comments

0.1

15.10.2015

Andrea Petta, Luigi Serra

First version

An architectural overview

Code control system

Prerequisites

Git

Keywords

Web Component

Polymer

webcomponents.js polyfills

Datalet

Datalet lifecycle

Controllet

DatalEts Ecosystem Provider (DEEP)

DEEPCLIENT

DEEPCLIENT Usage:

Components Tutorial (Polymer 1.0)

A behaviors based architecture

An example of datalet for Highchart js library

Ajax-Json-JsonPath behavior

Base datalet

base-ajax-json-jsonpath-datalet Datalet

Highcharts component

Linechart datalet

Barchart datalet

Datatable-datalet

Component documentation

Deploy a new component

Datalet directory structure

Deploy

MISC

Integrated Development Environment: phpStorm

Useful Links


An architectural overview

We’ll briefly introduce our Datalet architectural design.

We have three actors:

The DEEP-Client asks to DEEP for a specific Datalet, DEEP responds with the information necessary for the Datalet inclusion, the DEEP-Client retrieve the Datalet from DEEP-Component and include it into the page.

SPOD__1_.png

Code control system

Prerequisites

Git

Go to http://service.routetopa.eu:7480/users/sign_in and create an account.

Send an email to GIT system administrator (specifying your email account on GIT) to be added into WebCompDev group.

From terminal:

$ git config --global user.name “YOUR NAME”

$ git config --global user.mail “YOUR EMAIL ADDRESS”

 

Create a directory inside your public HTML web server directory, then :

$ git clone http://service.routetopa.eu:7480/isislab/DEEP.git

$ git clone http://service.routetopa.eu:7480/isislab/DEEPCLIENT.git

$ git clone http://service.routetopa.eu:7480/WebCompDev/COMPONENTS.git

now in your web server's local directory there are 3 new directory DEEP, DEEPCLIENT, COMPONENTS.

Now we have to configure DEEP and DEEPCLIENT to work correctly on your environment.

In DEEP directory edit file datalets.xml. Substitute the <components_repository_url_reference> value with the full URL of COMPONENTS/datalets just cloned, repeat the same operation for controllets.xml.

In DEEPCLIENT edit file index.html and substitute ComponentService.deep_url with the correct DEEP URL.

Now you can test the Datalet demo pointing the browser to DEEPCLIENT/index.html


Keywords

Web Component

Are a set of standards currently being produced by Google engineers as a W3C specification that allow for the creation of reusable widgets or components in web documents and web applications.
Web Components consist of 4 main elements which can be used separately or all together:

Polymer

The Polymer library developed by Google is designed to make easier and faster for developers to create reusable components for the modern web.

webcomponents.js polyfills

Are a set of polyfills built on top of the Web Components specifications. It makes possible for developers to use these standards today across all modern browsers. As these technologies are implemented in browsers, the polyfills will shrink and you'll gain the benefits of native implementations. webcomponents.js automatically detects native support and switches to the fast path when available. Your elements seamlessly start relying on the native implementation.

Datalet

It‘s responsible for data retrieving, filtering and visualization. From technological point of view it’s a web component built with polymer library. In our architecture each Datalet inherit from a base web component: base-datalet.

The base-datalet have two main attributes: data_url and query. The data_url attribute specify the data url from which to take data. This data should be json formatted and available by ajax request. The query attribute is a string which represent a selection and, optionally, filtering to apply to retrieved data.

Datalet lifecycle

Each Datalet follow this lifecycle inherit from base-datalet:

  1. Data retrieving through an ajax request
  2. Data selection
  3. Data filtering (optional)
  4. Data transformation
  5. Data presentation

Controllet

It’s a web component created with Polymer library that implements control behaviour like form, slider, etc. It generally use a set of polymer core elements to realize a more complex component using composition paradigm.

DatalEts Ecosystem Provider (DEEP)

The DatalEts Ecosystem Provider (DEEP) is a RESTful web service written in PHP. It make available a public discovery service that retrieve all the Datalet available into the system and a service call that provide a specific Datalet.

DEEPCLIENT

Is a simple javascript library that allows the connection with DEEP and the inclusion of a Datalet/Controllet within a HTML page.

DEEPCLIENT Usage:

This is a minimal example of deep client usage.

<html>
<head>

<script type="text/javascript" src="js/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="js/webcomponents.js"></script>
<script type="text/javascript" src="js/deepClient.js"></script>

 <script type="text/javascript">
        jQuery(document).ready(function($) {
           var datalet_params =

              {
              component  
: "DATALET_NAME",

      params : {
                data
-url        : "DATA_URL",

layou-param-1 : "LAYOUT-VALUE",

      }
    fields      
: Array("FIELD1", "FIELD2"),
    placeHolder
: "HTML_PLACEHOLDER"

};

           ComponentService.deep_url = 'DEEP_URL';
          
ComponentService.getComponent(datalet_params);


   
});


 
</script>

</head>

<body>

<div id="HTML_PLACEHOLDER"></div>

</body>

</html>

We create an empty HTML document with a single DIV. On document ready event we create a datalet_params object, this object contains information for Datalet retrieving and initialization.

datalet_params contains :

ComponentService.getComponent function take a datalet_params object as parameter. This function is responsible for Datalet info retrieving from DEPP and Datalet/Controllet code retrieving from DEEPCOMPONENTS. Once the Datalet/Controllet code is available DEEPCLIENT add the Datalet to the document DOM and initialize the Datalet with the params parameters. The Datalet/Controllet will use the fields data for the selection step.

Now the Datalet is able to complete its lifecycle.

The programmer must specify the DEEP endpoint through the variable ComponentService.deep_url.
e.g.: ComponentService.deep_url = 'http://deep.routetopa.eu/DEEP/';


Components Tutorial (Polymer 1.0)

[Knowledge required : javascript, html, Polymer 1.0]

A behaviors based architecture

Since Polymer 1.0 doesn’t support components inheritance, we provide a way to have it in datalets, waiting for it in the next Polymer version.

We provide a bundle of objects that implements all behaviors we want our datalets have. They are divided in two categories : BaseDataletsBehaviors and CustomDataletBehaviors.

The BaseDataletBehaviors define the lifecycle for all Datalets. Every lifecycle step in BaseDataletBehaviors  is implemented in the CustomDataletBehaviors. This mechanism provides a components hierarchy that standardize Datalet production and make reusable common behaviors.

 

The BaseDataletBehaviors are:

The BaseDataletBehavior define the mandatory attributes that all datalet must have :

var BaseDataletBehavior = {

   properties: {

       /**

        * It represent the data url from CKAN api

        *

        * @attribute dataUrl

        * @type string

        * @default 'null'

        */

       dataUrl: {

           type: String,

           value: ""

       },

       /**

        * It represents one or multiple fields selected by user

        *

        * @attribute fields

        * @type String

        * @default empty

        */

       fields: {

           type: String,

           value: ""

       },

       /**

        * The selected and transformed data you can use in presentation phase

        *

        * @attribute data

        * @type Array

        * @default empty

        */

       data: {

           type: Array,

           value: []

       }

   },

   factoryImpl: function(data_url, fields) {

       this.data_url = data_url;

       this.fields   = fields;

   }

};

The WorkcycleBehavior implements the workcycle of each datalets, composed by this steps :

var WorkcycleBehavior = {

   _component: null,

   /**

    * Request data from source(e.g. CKAN by api) using some kind of technology(e.g. Ajax)

    *

    * @method requestData

    */

   requestData: function(){

   },

   /**

    * Select the fields from data(typically json) previously retrieved by ajax request. The selection could be done by jsonPath but

    * it depends on the representation data format(CKAN apies return a json representation of the dataset).

    *

    * @method selectData

    */

   selectData: function(){

   },

   /**

    * Filter data previously selected. An example of filterting could be an expression such "fields > 30" or "fields = 'AAA'"

    * If you are using jsonPath to select the datas you can apply an expression directly in the jsonPath query string.

    *

    * @method filterData

    */

   filterData: function(){

   },

   /**

    * Transform the selected data in order to build the structure that the presentation phase needs.

    *

    * @method transformData

    */

   transformData: function(){

   },

   /**

    * Build the object/s for presentation layer.

    *

    * @method presentData

    */

   presentData: function(){

   },

   /**

    * This method represents the entire datalet workcycle.

    *

    * @method runWorkcycle

    */

   runWorkcycle: function() {

       this.selectData();

       this.filterData();

       this.transformData();

       this.presentData();

   },

   /**

    * This method save the reference to the polymer object related to the datalet.

    *

    * @method init

    */

   init: function(component){

       this._component = component;

       this.requestData();

   }

};

 

The CustomDataletBehaviors represents the specific implementation for the work-cycle steps.

This behaviours are a components hierarchy that use a particular communication channel, data format and visualization library.

An example of datalet for Highchart js library

We’ll describe for example purpose a Datalet that use a AJAX channal, handle JSON data format and use Highcharts for data visualization and JSONPATH library for data selection.

Ajax-Json-JsonPath behavior

Here we exploit an AJAX communication channel that handle JSON data format and extends the base behaviors (in particular the requestData method). This behavior override the select method in order to perform a JSONPATH query according to the user selected fields stored in the this._component.fields variable.

var AjaxJsonJsonPathBehavior = {

   properties: {

       /**

        * It contains the json data from async xhr call returned from core-ajax core component

        *

        * @attribute json_results

        * @type object

        * @default 'null'.

        */

       json_results: {

           type: Object,

           value: {}

       }

   },

   /**

    * Make an AJAX call to the dataset URL

    *

    * @method requestData

    */

   requestData: function(){

       var comp = this;

       $.ajax({

           url: this._component.dataUrl,

           dataType: "json",

           success: function(e){

               comp.handleResponse(e);

           }

       });

   },

   /**

    * Called when core-ajax component receive the json data from called url.

    *

    * @method handleResponse

    */

   handleResponse: function(e) {

       this.properties.json_results.value = e;

       this.runWorkcycle();

   },

   /**

    * Check if input field(passed as an array of separated value that mach with field path in received object) is an array of objet.

    * The field is checked on current json object retrieved from the async request.

    *

    * @param field

    */

   isFieldArray : function(field){

      if(field.length == 0) return false;

      var obj = this.properties.json_results.value[field[0]];

      for(var i=1; i < field.length; i++){

         obj = (obj.constructor == Array) ? obj[0][field[i]] : obj[field[i]];

      }

      return (obj.constructor === Array && obj[0].constructor == Object) ? true : false;

   },

   /**

    * selectData built a JSONPATH query based on the user selected fields then extract data from the JSON response.

    * This method built an objects <name, data> for every user selected field and push it into the data array.

    *

    * @method selectData

    */

   selectData : function(){

       this.data = [];

       this._component.fields = JSON.parse(this._component.fields);

       for(var i=0;i < this._component.fields.length; i++){

           var query = "$";

           var query_elements = this._component.fields[i].split(',');

           for(var j=0; j < query_elements.length;j++){

               query += "['" + query_elements[j] + "']";

               if(this.isFieldArray(query_elements.slice(0,j+1))){

                   query += "[*]";

               }

           }

           this.data.push({name : query_elements[query_elements.length - 1], data : jsonPath(this.properties.json_results.value, query)});

       }

   }

};

Base datalet

We define a base-datalet that include BaseDataletBehavior and a footer that contain a ROUTE-TO-PA logo and data source domain information.

<link rel="import" href="../../bower_components/polymer/polymer.html">

<dom-module id="base-datalet">

   <template>

       <style>

           #footer {

               position: relative;

               bottom: 0;

               width: 100%;

               font-size: small;

           }

           #rtpalogo{

               height:30px;

               width: 30px;

           }

       </style>

       <b>Source : </b><span id="domain"></span><br><br>

       <div id="footer">Powered by Route-to-PA&nbsp&nbsp<a href="http://routetopa.eu/"><img src="static/images/rtpalogo.png" id="rtpalogo"></a></div><br>

   </template>

   <script src="static/js/BaseDataletBehaviors.js"></script>

   <script>

       BaseDatalet = Polymer({

           is: 'base-datalet',

           /**

            * It is called after the element's template has been stamped and all elements inside the element's local

            * DOM have been configured (with values bound from parents, deserialized attributes, or else default values)

            * and had their ready method called.

            *

            * Extract the dataset domain from the entire URL and set the text content of the datalet footer.

            *

            * @method ready

            *

            */

           ready: function(){

               this.$.domain.textContent = this.dataUrl.split("/")[0] + "//" + this.dataUrl.split("/")[2];

           }

       });

   </script>

</dom-module>

All components that import this one will include the BaseDataletBehavior.

base-ajax-json-jsonpath-datalet Datalet

<link rel="import" href="../base-datalet/base-datalet.html">

<dom-module id="base-ajax-json-jsonpath-datalet">

   <template>

       <base-datalet data-url="{{dataUrl}}" fields="{{fields}}"></base-datalet>

   </template>

   <script src="static/js/vendor/jsonpath-0.8.5.js"></script>

   <script src="static/js/AjaxJsonJsonPathBehavior.js"></script>

   <script>

       BaseDatalet = Polymer({

           is: 'base-ajax-json-jsonpath-datalet'

       });

   </script>

</dom-module>

All components that import this one will include AjaxJsonJsonPathBehavior and JSONPath library. Since this Datalet handle JSON data, we use JSONPath (http://goessner.net/articles/JsonPath/) to select specific fields from entire dataset (an example of query is $.result.records..Supplier $.result.records..Amount). For a new datatype a new behavior and new way to select the fields should be provided.

Highcharts component

The component we’ll present in this section include Highcharts javascript library (http://www.highcharts.com/) to visualize data in a several type of charts.

Since all selected example charts use the same way to select the data to build the categories and series objects, we built a super Highchart component which all our specific chart datalet will “extend” in order to share the selection mechanism.

 

<link rel="import" href="../base-ajax-json-jsonpath-datalet/base-ajax-json-jsonpath-datalet.html">

<dom-module id="highcharts-datalet">

   <template>

       <div id="container" style="width:auto; height:auto;"></div>

       <base-ajax-json-jsonpath-datalet data-url="{{dataUrl}}" fields="{{fields}}"></base-ajax-json-jsonpath-datalet>

   </template>

   <script src="static/js/highcharts.js"></script>

   <script src="static/js/exporting.js"></script>

   <script>

       var HighchartsBehavior = {

           properties: {

               categories: {

                   type: Array,

                   value: []

               },

               series: {

                   type: Array,

                   value: []

               },

               series_type:{

                   type: String,

                   value: "line"//spline,time

               }

           },

           /**

            * Populate the categories and the series array.

            *

            * @method transformData

            *

            * @param {Event} e

            */

           transformData: function () {

               this.properties.categories.value = this.data[0].data;

               for (var i = 1; i < this.data.length; i++)

               {

                   this.data[i].data.every(function (element, index, array) {

                       try {

                           // TODO fix this parsing mechanism

                           (isNaN(parseFloat(element))) ? array[index] = parseFloat(element.match(/[0-9]+/g).join(".")) :

                                   array[index] = parseFloat(element);

                       }catch(e){

                           //console.log("Parsing data error. Highchart-datalet.selectData");

                       }

                       return true;

                   });

                   this.properties.series.value.push(this.data[i]);

               }

           }

       };

       var HighchartsComponentBehavior = $.extend(true, {}, BaseDataletBehavior, WorkcycleBehavior, AjaxJsonJsonPathBehavior, HighchartsBehavior);

       HighchartsDatalet = Polymer({

           is : 'highcharts-datalet'

       });

   </script>

</dom-module>

First we have to import the base-ajax-json-jsonpath-datalet, in order to extend its behavior and include its DOM:

<link rel="import" href="../base-datalet/base-datalet.html">

In the template section (the DOM part of our new datalet) we include the superclass DOM (as footer) and create a div as placeholder for chart object.

<template>

   <div id="container" style="width:auto; height:auto;"></div>

   <base-ajax-json-jsonpath-datalet data-url="{{dataUrl}}" fields="{{fields}}"></base-ajax-json-jsonpath-datalet>

</template>

   

At this point we include the Highchart javascript library used by all chart datalets which use Highchart as visualization library.

<script src="static/js/highcharts.js"></script>

<script src="static/js/exporting.js"></script>

Now we define a new behavior with all attribute that the specialized chart datalets can share (categories and series) and the implementation of a selection method.

var HighchartsBehavior = {

   properties: {

       categories: {

           type: Array,

           value: []

       },

       series: {

           type: Array,

           value: []

       },

       series_type:{

           type: String,

           value: "line"//spline,time

       }

   },

   /**

    * Populate the categories and the series array.

    *

    * @method transformData

    *

    * @param {Event} e

    */

   transformData: function () {

       this.properties.categories.value = this.data[0].data;

       for (var i = 1; i < this.data.length; i++)

       {

           this.data[i].data.every(function (element, index, array) {

               try {

                   // TODO fix this parsing mechanism

                   (isNaN(parseFloat(element))) ? array[index] = parseFloat(element.match(/[0-9]+/g).join(".")) :

                           array[index] = parseFloat(element);

               }catch(e){

                   //console.log("Parsing data error. Highchart-datalet.selectData");

               }

               return true;

           });

           this.properties.series.value.push(this.data[i]);

       }

   }

};

We are overriding the transformData method of WorkcycleBehavior so to access to data attribute we have to use the reference to the Polymer component (this._component). This reference will be set by the final component that implements all workcycle (that visualize the chart in our example), as we will show you later. As you can notice in the transformData function we can access to json_results attribute defined in the Behavior that implements the communication channel.

After defined the Behavior for Highchart component we have to extend it to include the BaseDataletBehavior, WorkcycleBehavior, AjaxJsonJsonPathBehavior to crate a new behavior specific for our new Datalet. The order of the extension is crucial (see JQuery $.extend docs).

var HighchartsComponentBehavior = $.extend(true, {}, BaseDataletBehavior, WorkcycleBehavior, AjaxJsonJsonPathBehavior, HighchartsBehavior);

The last thing to do is to create  the Polymer javascript object.

HighchartsDatalet = Polymer({

   is : 'highcharts-datalet'

});

Linechart datalet

The linechart-datalet will use the highchart-component to visualize a linechart. To do this we override the presentData method, in a new behavior(LinechartBehavior),  in order to use the categories and series attributes, generated by “super class”, to build the chart.

<link rel="import" href="../highcharts-datalet/highcharts-datalet.html">

<dom-module id="linechart-datalet">

   <template>

       <highcharts-datalet id="charts" data-url="{{dataUrl}}" fields="{{fields}}"></highcharts-datalet>

   </template>

   <script>

       var LinechartBehavior = {

           /**

            * Bluid Highchart object

            *

            * @method presentData

            */

           presentData: function(){

               $(this._component.$.charts.$.container).highcharts({

                   title: {

                       text: "" + this._component.title

                   },

                   chart: {

                       zoomType: 'xy'

                   },

                   xAxis: {

                       categories: this.properties.categories.value,//this._component.categories,

                       title: {

                           text: this._component.xAxisLabel

                       }

                   },

                   yAxis: {

                       title: {

                           text: this._component.yAxisLabel

                       },

                       plotLines: [{

                           value: 0,

                           width: 1,

                           color: '#808080'

                       }]

                   },

                   tooltip: {

                       valueSuffix: ' ' + this._component.suffix

                   },

                   legend: {

                       layout: 'vertical',

                       align: 'right',

                       verticalAlign: 'top',

                       x: -40,

                       y: 100,

                       floating: true,

                       borderWidth: 1,

                       backgroundColor: ((Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'),

                       shadow: true

                   },

                   credits: {

                       enabled: false

                   },

                   series: this.properties.series.value

               });

           }

       };

       LinechartDatalet = Polymer({

           is: 'linechart-datalet',

           properties: {

               /**

                * It's the label for X axis

                *

                * @attribute xAxisLabel

                * @type String

                * @default ''

                */

               xAxisLabel: {

                   type: String,

                   value: ""

               },

               /**

                * It's the label for Y axis

                *

                * @attribute yAxisLabel

                * @type String

                * @default ''

                */

               yAxisLabel: {

                   type: String,

                   value: ""

               },

               /**

                * It's the title of the chart

                *

                * @attribute title

                * @type Strig

                * @default ''

                */

               title: {

                   type: String,

                   value: "Heading"

               },

               /**

                * It's the values suffix

                *

                * @attribute suffix

                * @type Strig

                * @default 'units'

                */

               suffix : {

                   type : String,

                   value : "units"

               }

           },

           /**

            * 'ready' callback extend the LinechartComponentBehavior with HighchartsComponentBehavior and LinechartBehavior

            * and run the Datalet workcycle.

            *

            * @method ready

            */

           ready: function(){

               var LinechartComponentBehavior = $.extend(true, {}, HighchartsComponentBehavior, LinechartBehavior);

               LinechartComponentBehavior.init(this);

           }

       });

   </script>

</dom-module>

 

After creating the main component behavior (LinechartComponentBehavior)  extending the HighchartComponentBehavior previously created, we have to initialize it by passing the reference to Polymer object associated to linechart-datalet. We do it  in the ready handler of Polymer object.

LinechartDatalet = Polymer({

   is: 'linechart-datalet',

   /**

    * 'ready' callback extend the LinechartComponentBehavior with HighchartsComponentBehavior and LinechartBehavior

    * and run the Datalet workcycle.

    *

    * @method ready

    */

   ready: function(){

       var LinechartComponentBehavior = $.extend(true, {}, HighchartsComponentBehavior, LinechartBehavior);

       LinechartComponentBehavior.init(this);

   }

});

The ready handler define a lifecycle callback invoked when all local DOM children of this element have been created and "configured" with data bound from this element, attribute values, or defaults.

The base behavior will use this reference to get information from the Polymer component like passed attributes, DOM, etc.

 

LinechartDatalet has xAxisLabe,l yAxisLabel, title and suffix properties. This properties are used as layout params, and define respectively the label for the x-axis, y-axis, the title of the chart and the value suffix .

Barchart datalet

The barchart-datalet will use the highchart-datalet to visualize a barchart.

<link rel="import" href="../highcharts-datalet/highcharts-datalet.html">

<dom-module name="barchart-datalet">

   <template>

       <highcharts-datalet id="charts" data-url="{{dataUrl}}" fields="{{fields}}"></highcharts-datalet>

   </template>

   <script>

       var BarchartBehavior = {

           /**

            * Bluid Highchart object

            *

            * @method transformData

            */

           presentData: function(){

               $(this._component.$.charts.$.container).highcharts({

                   chart: {

                       type: 'bar',

                       zoomType: 'xy'

                   },

                   title: {

                       text: this._component.title

                   },

                   xAxis: {

                       categories: this.properties.categories.value,

                       title: {

                           text: this._component.xAxisLabel

                       }

                   },

                   yAxis: {

                       min: 0,

                       title: {

                           text: this._component.yAxisLabel,

                           align: 'high'

                       },

                       labels: {

                           overflow: 'justify'

                       }

                   },

                   tooltip: {

                       valueSuffix: ' ' + this._component.suffix

                   },

                   plotOptions: {

                       bar: {

                           dataLabels: {

                               enabled: true

                           }

                       }

                   },

                   legend: {

                       layout: 'vertical',

                       align: 'right',

                       verticalAlign: 'top',

                       x: -40,

                       y: 100,

                       floating: true,

                       borderWidth: 1,

                       backgroundColor: ((Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'),

                       shadow: true

                   },

                   credits: {

                       enabled: false

                   },

                   series: this.properties.series.value

               });

           }

       };

       BarchartDatalet = Polymer({

           is: 'barchart-datalet',

           properties: {

               /**

                * It's the label for X axis

                *

                * @attribute xAxisLabel

                * @type String

                * @default ''

                */

               xAxisLabel: {

                   type: String,

                   value: ""

               },

               /**

                * It's the label for Y axis

                *

                * @attribute yAxisLabel

                * @type String

                * @default ''

                */

               yAxisLabel: {

                   type: String,

                   value: ""

               },

               /**

                * It's the title of the chart

                *

                * @attribute title

                * @type Strig

                * @default ''

                */

               title: {

                   type: String,

                   value: "Heading"

               },

               /**

                * It's the values suffix

                *

                * @attribute suffix

                * @type Strig

                * @default 'units'

                */

               suffix : {

                   type : String,

                   value : "units"

               }

           },

           /**

            * 'ready' callback extend the BarchartComponentBehavior with HighchartsComponentBehavior and BarchartBehavior

            * and run the Datalet workcycle.

            *

            * @method ready

            */

           ready: function(){

               var BarchartComponentBehavior = $.extend(true, {}, HighchartsComponentBehavior, BarchartBehavior);

               BarchartComponentBehavior.init(this);

           }

       });

   </script>

</dom-module>

Datatable-datalet

To be more complete we present another example of datalet that is slightly different from previous ones: a table based datalet which use Datatables javascript library (https://www.datatables.net/) to render a table. The main difference is that datatable-datalet component has a dynamic DOM built according to the size of data.

<link rel="import" href="../base-ajax-json-jsonpath-datalet/base-ajax-json-jsonpath-datalet.html">

<dom-module id="datatable-datalet">

   <template>

       <link rel="stylesheet" href="js/DataTables-master/media/css/jquery.dataTables.min.css">

       <table id="datatable" class="table table-striped table-bordered" cellspacing="0" style="height: auto; width: auto;">

       </table>

       <base-ajax-json-jsonpath-datalet data-url="{{dataUrl}}" fields="{{fields}}"></base-ajax-json-jsonpath-datalet>

   </template>

   <script src="js/DataTables-master/media/js/jquery.dataTables.js"></script>

   <script>

       var DatatableBehavior = {

           presentData: function(){

               if(!this.data || this.data == undefined) return;

               html = "";

               html += '<thead>'+

                          '<tr>';

               for(var x = 0; x<this.data.length; x++){

                  html += '<th>' + this.data[x].name + '</th>';

               }

               html +=  '</tr>' +

                        '</thead>' +

                        '<tfoot>' +

                        '<tr>';

               for(var x = 0; x<this.data.length; x++){

                   html += '<th>' + this.data[x].name + '</th>';

               }

               html += '</tr>' +

                       '</tfoot>' +

                       '<tbody>';

               for(var i = 0; i<this.data[0].data.length; i++){

                   html += '<tr>';

                   for(var x = 0; x<this.data.length; x++){

                     html += '<td>' + this.data[x].data[i] + '</td>';

                   }

                   html += '</tr>';

               }

               html += '</tbody>';

               $(this._component.$.datatable).append(html);

               $(this._component.$.datatable).DataTable();

           }

       };

       Polymer({

           is : 'datatable-datalet' ,

           ready: function(){

               var DatatableComponentBehavior =  $.extend(true, {}, BaseDataletBehavior, WorkcycleBehavior, AjaxJsonJsonPathBehavior, DatatableBehavior);

               DatatableComponentBehavior.init(this);

           }

       });

   </script>

</dom-module>

Component documentation

To document a web component you can use a iron-component-page polymer component (https://elements.polymer-project.org/elements/iron-component-page). Each component must be documented to make DEEP able to give information about available components and their usage.

Documentation can be encoded into html comments (<!-- ... -->) or using JsDoc notation (/** ... */). Markdown format is supported.

Once you have properly commented the code to include the new component (datalet or controllet) to documentation you have to add the component path to source attribute.

Deploy a new component

Here are described all the mandatory steps for Datalet deploy on DEEP.

Datalet directory structure

datalet-name

|------------datalet-name.png

|------------datalet-name.html

|------------demo

|                |-----index.html

|------------docs.html

|------------js*

|------vendor*

|------files.js*

|------------images*

Deploy

To deploy a new component you have to :

<component>

   <name>linechart-datalet</name>

   <attributes>

       <attribute>

           <name>data-url</name>

       </attribute>

       <attribute>

           <name>fields</name>

       </attribute>

   </attributes>

   <idm>

       <inputs>

           <input>

               <name>Categories</name>

               <description>The chart categories. Its values will be put in the horizontal axis. You need one value for each series.</description>

               <scale>nominal</scale>

               <role>domain</role>

               <selection>11</selection>

           </input>

           <input>

               <name>Series</name>

               <description>The chart series. Its values will be put in the vertical axis.</description>

               <scale>nominal</scale>

               <role>domain</role>

               <selection>11</selection>

           </input>

           <layouts>

               <input>

                   <name>title</name>

                   <description>The label for the title of the chart</description>

               </input>

               <input>

                   <name>x-axis-label</name>

                   <description>The label for the X axis</description>

               </input>

               <input>

                   <name>y-axis-label</name>

                   <description>The label for the Y axis</description>

               </input>

               <input>

                   <name>suffix</name>

                   <description>The values suffix(eg units, dollars, euro, ...)</description>

               </input>

           </layouts>

       </inputs>

   </idm>

</component>

You should expose all attributes (adding attributes tags) in order to make the DEEP able to give this information when your component will be requested.

MISC

Integrated Development Environment: phpStorm

As an Integrated Development Environment (IDE) for PHP and web  component,  JetBrains Phpstorm (https://www.jetbrains.com/phpstorm/ ) has been chosen, but any other ide can be used. PhpStorm provides an editor as support for PHP, HTML, Javascript, CSS and many other languages.

Useful Links

Description

Link

Web Components

http://webcomponents.org/ 

Polymer

https://www.polymer-project.org/1.0/ 

Google Web Component: a collection of web component for Google APIs, built with Polymer

https://googlewebcomponents.github.io/ 

Web Components Current Status

http://www.w3.org/standards/techs/components#w3c_all

Introduction to Web Components

http://www.w3.org/TR/components-intro/