Using D3 or third party libraries with Polymer – simple example

Off late I have been exploring and working with Polymer a lot. One thing I tried recently was to create a custom element (a custom tag) that has a d3 chart integrated into it. So whenever I use this element in an application the chart renders.

For those who are not familiar with Polymer, let me tell you, it’s a game changer for the next generation of web applications. The web is getting componentized with the new standards that are coming along – Shadow DOM, custom elements, templating, HTML imports e.t.c. Polymer is a polyfill layer or as they call it a SUGARING layer on top of these web standards that allow you create custom web components easily. You do not need to go the the low level API’s to create a custom element. Just use the Polymer library and start creating your own components. Also it provides a heck of other features that can be used, such as two way data bindings. So go over to the Polymer site and check it out.

Coming back to our topic of discussion, one thing that can be a bit confusing to Polymer beginners is to use a third party javascript library with your polymer element. Here I have an example of using the D3 js charting library to create a custom chart component. Let’s call our custom element  my-custom-d3-chart. By the end of this discussion we will have a custom tag – <my-custom-d3-chart />. Yes a custom HTML tag. Our own tag. Yo!!!

I will not go into the details of using Polymer and creating custom components. I will stick to the title of this discussion that says how to use a third party library with Polymer to create a custom element. So, let’s jump straight into some code,

my-custom-d3-chart.html

<link rel="import" href="../bower_components/polymer/polymer.html"> <!-- Import Polymer element -->
<script type="text/javascript" src="d3/d3.min.js"></script> <!-- Import d3 js -->

<polymer-element name="my-custom-d3-chart" attributes="data">  <!-- Define your custom element -->
<template>
  <!-- Shadow CSS -->
  <style type="text/css">
    div.bar {
      display: inline-block;
      width: 50px;
      height: 75px;
      background: teal;
      margin-right: 5px;
    }
  </style>

  <!-- Shadow DOM -->
  <div id="holder"></div>
</template>

<script>
  Polymer("my-custom-d3-chart", {
    ready: function() {   //ready() will be called once Polymer is ready and element is initilized 
      var dataset = this.data;
      //d3 chart rendering code
      d3.select(this.$.holder).selectAll('div').data(dataset).enter().append('div').attr('class','bar').style("height", function(d)  {
        return d*2 + "px";
      });
    },
    data: [11, 35, 72, 9, 10] //default value for data array.
  });
</script>
</polymer-element>

Here we have some some code for our custom element file – my-custom-d3-chart.html. I will not go into every line. What is important for this discussion is the second line of code (bolded), which imports the d3 js library into my custom element. At the moment I have a static path that points to my local. To make our component more scalable and sharable you can add the CDN link of D3, so that every time somebody uses our custom component, there is no local dependency for D3. And for any such third party library imports place your line of code on the top of your custom element definition. That should do the job.

The second point that I wanted to highlight was d3.select(this.$.holder) towards the end of the code section. Now, when you are initializing a D3 chart you have to specify the reference of your container to d3.select() method. Now, if you use document.getElementById(“holder”) or document.querySelector(“#holder”) to get the reference and pass it to d3.select then it will not work, since holder is part of the Shadow DOM of our custom element and not directly part of the HTML DOM of the page. What it means is that holder will hide inside <my-custom-d3-chart> element and it will not be exposed to the outer world i.e page DOM. In fact holder will not be even considered as a child of <my-custom-d3-chart> element. However, there is way of getting the reference of holder and that is using Polymer’s automatic node finding feature. Hence this line this.$.holder will get me the reference of the holder. Now I can pass this to d3.select().

Once we have our custom element ready I can use it in my page. This is how to do it,
index.html

<!DOCTYPE html>
<html>
<head>
<!-- 1. Load platform.js for polyfill support. -->
<script src="bower_components/platform/platform.js"></script>

<!-- 2. Use an HTML Import to bring in our custom element. -->
<link rel="import" href="my-custom-d3-chart.html">

<style type="text/css">
  html, body {
    margin: 0;
    padding: 10px;
    height: 100%;
  }
  my-custom-d3-chart {
    display: block;
  }
</style>
</head>
<body>
  <my-custom-d3-chart></my-custom-d3-chart>   <!-- This is my awesome custom tag -->
</body>
</html>

Update:
There is a better way of importing 3rd party libraries and managing multiple component dependencies. For eg. I have two custom components that are using the same library, let’s say HighCharts JS, how do I make sure that highcharts js is downloaded only once. I will discuss on this in my second part to this post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s