June 12, 2016

Reasons to use Polymer with Redux

In the recent project we were building analytics platform using web components. At the beginning of the project we have chosen Polymer framework to build our custom web components. After 3-4 month of work we have discovered several downsides of the framework:
1) it's not mature enough and we still have issues with basic functionality. For example dom-repeater element is for sure basic structure but for some reason it doesn't update child element correctly all the time.
2) Polymer gives us 2-way data binding which in big projects could give performance issues.
3) 2 way data binding system complicates developing (you have to place conditional guarders literally in every watch function) and debugging process.

To solve all mentioned above problems we decided to use Redux state management system. How Redux can help us:
1) Easy debuging including time-travel debugging and hotswapping state
2) Redux devTool app for Chrome allow to investigate your state after each action.
3) separation of logic from ui renderers. Our views can listen to changes to various pieces of Redux state and update the DOM accordingly.

How to use Redux with Polymer:
There is 'connection bridge' between Polymer and Redux - polymer-redux behavior
Using this behavior Polymer elements can listen to the State changes and render themself accordingly. It means that all the logic would be moved to Redux reducers and Polymer elements would be responsible only for rendering and internal 'dumb' logic.

As Redux state is read-only we can not use Polymer 2-way data binding to connect to the state. The only way to change the state is to dispatch actions. Doing this we eventually will use 1-way data binding which is easier to understand and debug.

After 2 month working with Redux we have concluded that this is the right way to go but we have still some issues with Polymer basic functionality which make us think that React + Redux might be a better solution.

April 12, 2016

Histogram + line combo charts

Recently I have faced a problem of creating histogram + line combo chart. My first attempt was using google-charts but the problem is that according to the documentation it is not possible to use histogram type in a combo charts. the same problem I had with Plotly.js library.  Fortunately these libraries are very flexible so I tried to use vertical bar (column) chart instead of the histogram.

Fig.1. Bar chart
Fig.2. Histogram + line combo chart


                                     






Nevertheless  bar chart x-axis has ticks (labels) for every bar (see Fig.1) instead of the “from-to” range as in normal histogram or line chart (Fig.2). So the post is about how to fix that for both libraries. The main trick is in shift of the plot to the right. lets see how it’s done in codepen for google-chart and for plotly.js

code for google-chart is below:

google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawVisualization);
 
function drawVisualization() {
  var config = [{color: 'lightblue', label: 'Select'}, {color: 'blue', label: 'CTX'}, {color: 'green', label: '100%'}];
   
  var data = new google.visualization.DataTable();
  data.addColumn('number', 'Amount');
  data.addColumn({type: 'string', role: 'tooltip', 'p': {'html': true}});
  data.addColumn('number', config[0].label); 
  data.addColumn('number', config[1].label);
  data.addColumn('number', config[2].label);
  data.addRows([
     [0.5, null, 50, 80, 111],
     [1.5, null, 30, 170, 200],
     [2.5, null, 300, 300, 300],
     [3.5, null, 250, 310, 370],
     [4.5, null, 150, 240, 250]
  ]);
 
  // setting tooltip
  for (var i = 0, rows = data.getNumberOfRows(), month; i < rows; i++) {
      data.setValue(i, 1,`<div class="gc-tooltip">
           <div style="color:${config[0].color;">
                Select:<span class="gc-right">${data.getValue(i,2)}</span>
           </div>
           <div style="color:${config[1].color;">
                CTX:<span class="gc-right">${data.getValue(i,3)}</span>
           </div>
           <div style="color:${config[2].color;">
                100%:<span class="gc-right">${data.getValue(i,4)}</span>
           </div>
           </div>`);
  }
 
  var options = {
    vAxis: {
        title: 'Trace Frequency',
      gridlines: {color: 'gainsboro'}
    },
    hAxis: {
      title: 'Amount', format: '#K',
      ticks: [0, 1, 2 , 3, 4, 5],
      gridlines: {color: 'white'}
    },
    series: {
      0: {type: 'bars', color: config[0].color},
        1: {type: 'line', color: config[1].color},
        2: {type: 'line', color: config[2].color}
    },
    legend: "none",
    bar: {groupWidth: "98%"},
    pointSize: 8,
    focusTarget: 'category',
    tooltip: {isHtml: true},
    width: 600,
    height: 400,
  };
 
  var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
  chart.draw(data, options);
}
code for plotly.js is below:
var trace0 = {
  
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [111,200,300,370,250], 
  type: 'scatter',
  marker: {
    color: 'green'
  },
  hoverinfo: 'y'
};
 
var trace1 = {
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [80, 170, 300, 310, 240], 
  type: 'scatter',
  marker: {
    color: 'blue'
  },
  hoverinfo: 'y'
};
 
var trace2 = {
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [50,30,300,250,150],
  type: 'bar',
  marker: {
    color: 'lightblue'
  },
  hoverinfo: 'y'
};
 
var layout = {
  xaxis: {
        zeroline: false,
        title: 'Amount',
        range: [0, 5],
        type: 'linear',
        dtick: 1,
        ticksuffix: 'K'
  },
  yaxis: {
        title: 'Trace Frequency',
        range: [0, 400],
        type: 'linear'
  },
  bargap :0.01,
  margin: {
    pad: 10
  },
  showlegend: false,
  hovermode: 'x',
}
 
Plotly.newPlot('myDiv', [trace0, trace1, trace2], layout);

Bootstrap 3 carousel with multiple items.

As you probably know bootstrap 3 carousel shows only one item at a time.
But what if I want it to show 2 or more at a time? or even make it responsive?
I’ve searched the internet and have some solutions which for some reason didn’t work correctly.
Instead of sliding items one by one it was sliding all of the shown items together replacing them with a new set of items (so pretty much the same behavior as in single item carousel).
At that time I had bootstrap v 3.3.2.
After spending some time on investigation how to fix that I figured out that newest updates to the bootstrap 3 css and script files changes the behavior of carousel.
I’ve found that with bootstrap 3.2.0 multiple items carousel behaves as I would expect this.

here is a codepen example of responsive multiple items carousel using bootstrap 3.2.0.
the code is below:

Html:
<div class="container">
 <div class="row">
  <div class="col-xs-11 col-md-10 col-centered">

   <div id="carousel" class="carousel slide" data-ride="carousel" data-type="multi" data-interval="2500">
    <div class="carousel-inner">
     <div class="item active">
      <div class="carousel-col">
       <div class="block red img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block green img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block blue img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block yellow img-responsive"></div>
      </div>
     </div>
    </div>

    <!-- Controls -->
    <div class="left carousel-control">
     <a href="#carousel" role="button" data-slide="prev">
      <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
      <span class="sr-only">Previous</span>
     </a>
    </div>
    <div class="right carousel-control">
     <a href="#carousel" role="button" data-slide="next">
      <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
      <span class="sr-only">Next</span>
     </a>
    </div>
   </div>

  </div>
 </div>
</div>
Js:

$('.carousel[data-type="multi"] .item').each(function() {
 var next = $(this).next();
 if (!next.length) {
  next = $(this).siblings(':first');
 }
 next.children(':first-child').clone().appendTo($(this));

 for (var i = 0; i < 2; i++) {
  next = next.next();
  if (!next.length) {
   next = $(this).siblings(':first');
  }

  next.children(':first-child').clone().appendTo($(this));
 }
});
Css:

.col-centered {
    float: none;
    margin: 0 auto;
}

.carousel-control { 
    width: 8%;
    width: 0px;
}
.carousel-control.left,
.carousel-control.right { 
    margin-right: 40px;
    margin-left: 32px; 
    background-image: none;
    opacity: 1;
}
.carousel-control > a > span {
    color: white;
   font-size: 29px !important;
}

.carousel-col { 
    position: relative; 
    min-height: 1px; 
    padding: 5px; 
    float: left;
 }

 .active > div { display:none; }
 .active > div:first-child { display:block; }

/*xs*/
@media (max-width: 767px) {
  .carousel-inner .active.left { left: -50%; }
  .carousel-inner .active.right { left: 50%; }
 .carousel-inner .next        { left:  50%; }
 .carousel-inner .prev       { left: -50%; }
  .carousel-col                { width: 50%; }
 .active > div:first-child + div { display:block; }
}

/*sm*/
@media (min-width: 768px) and (max-width: 991px) {
  .carousel-inner .active.left { left: -50%; }
  .carousel-inner .active.right { left: 50%; }
 .carousel-inner .next        { left:  50%; }
 .carousel-inner .prev       { left: -50%; }
  .carousel-col                { width: 50%; }
 .active > div:first-child + div { display:block; }
}

/*md*/
@media (min-width: 992px) and (max-width: 1199px) {
  .carousel-inner .active.left { left: -33%; }
  .carousel-inner .active.right { left: 33%; }
 .carousel-inner .next        { left:  33%; }
 .carousel-inner .prev       { left: -33%; }
  .carousel-col                { width: 33%; }
 .active > div:first-child + div { display:block; }
  .active > div:first-child + div + div { display:block; }
}

/*lg*/
@media (min-width: 1200px) {
  .carousel-inner .active.left { left: -25%; }
  .carousel-inner .active.right{ left:  25%; }
 .carousel-inner .next        { left:  25%; }
 .carousel-inner .prev       { left: -25%; }
  .carousel-col                { width: 25%; }
 .active > div:first-child + div { display:block; }
  .active > div:first-child + div + div { display:block; }
 .active > div:first-child + div + div + div { display:block; }
}

.block {
 width: 306px;
 height: 230px;
}

.red {background: red;}

.blue {background: blue;}

.green {background: green;}

.yellow {background: yellow;}