How to create bar chart with path property in d3js? - d3.js

Below is how I creat a bar chart using rects in D3. However, how would I modify that to get the bar chart with path property in d3js?
I have a json file which has the data to be read and am trying to create a bar chart with paths rather than rectangles in d3js.
Json :
[
{
"name": "sam",
"age": 24
},
{
"name": "baby",
"age": 23
},
{
"name": "adu",
"age": 21
},
{
"name": "ja",
"age": 23
},
{
"name": "mack",
"age": 34
}
]
Code:
<script>
d3.json("mydata.json", function (data) {
var canvas = d3.select('body').append('svg')
.attr('width', 500)
.attr('height', 500);
canvas.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('width',function (d) { return d.age * 10; })
.attr('height', 48)
.attr('y', function (d, i) { return i * 50; })
.attr('fill', 'blue');
canvas.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('fill','white')
.attr('y', function (d, i) {
return i* 50 + 24;
})
.text(function (d) {
return d.name;
})
});
</script>
I have searched through many sites. I am unable to get though

You can't assign a d attribute to a rectangle, but you can make a bar chart out of paths rather than rectangles. All you need to do is know the coordinates of the corners of the rectangle. You really only need two corners on opposite sides. If you had top left and bottom right coordinates ([x0,y0] and [x1,y1] respectively) you could do something like:
function drawRect(x0,y0,x1,y1) {
var p1 = x0 + " " + y0;
var p2 = x0 + " " + y1;
var p3 = x1 + " " + y1;
var p4 = x1 + " " + y0;
var l = "L"; // cause I'm lazy.
return "M"+p1+l+p2+l+p3+l+p4+"Z";
}
This moves the cursor to p1, then draws connecting lines from p1 to p2 to p3 to p4 and then returns to the start with Z.
With scaled values this could get a bit verbose either passing scaled parameters or scaling the values in the path making function (as I do below).
This might look like (paths fade in so you can see that they match the rects):
var data = [1,2,3,5,8,3];
var width = 500;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleBand()
.domain(d3.range(data.length))
.range([0,width]);
var y = d3.scaleLinear()
.domain([0,d3.max(data)])
.range([height,0]);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", x.bandwidth())
.attr("height", function(d) { return height-y(d); })
.attr("x", function(d,i) { return x(i); })
.attr("y", function(d) { return y(d); })
svg.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("d", makeRect)
.attr("fill","orange")
.style("opacity",0)
.transition()
.style("opacity",1)
.duration(1500);
function makeRect(d,i) {
var x0 = x(i);
var y0 = y(d);
var x1 = x(i) + x.bandwidth();
var y1 = height;
var p1 = x0 + " " + y0;
var p2 = x0 + " " + y1;
var p3 = x1 + " " + y1;
var p4 = x1 + " " + y0;
var l = "L";
return "M"+p1+l+p2+l+p3+l+p4+"Z";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
But, this can't really be a preferred option in most cases. It is more complex than the rectangles for sure. The rectangle should be preferable, unless of course you are trying to do some sort of manipulation on the path that you can't do with a rectangle, or maybe you want rounded edges. Perhaps you are projecting the path on a globe or maybe you want to do some path transition (I've added extra vertices to the rectangle path to smooth the transition, in most cases, ideally the start and end paths of a transition have the same number of vertices):
var data = [1,2,3,5,8,3];
var width = 500;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleBand()
.domain(d3.range(data.length))
.range([0,width]);
var y = d3.scaleLinear()
.domain([0,d3.max(data)])
.range([height,0]);
svg.selectAll("path")
.data(data)
.enter()
.append("path")
.attr('d', d3.symbol().type( d3.symbols[1]).size(function(d){ return d*100; }) )
.attr("transform","translate(0,"+height/2+")")
.transition()
.attr("transform",function(d,i) { return "translate("+(x(i)+x.bandwidth()/2) + "," + height/2 + ")" })
.attr("fill","steelblue")
.duration(1500)
.transition()
.attr("d", makeRect)
.attr("fill","orange")
.attr("transform","translate(0,0)")
.duration(1500);
//*/
function makeRect(d,i) {
var x0 = x(i);
var y0 = y(d);
var x1 = x(i) + x.bandwidth();
var y1 = height;
var p1 = x0 + " " + y0;
var p2 = x0 + " " + y1;
var p3 = x1 + " " + y1;
var p4 = x1 + " " + y0;
var l = "L";
return "M"+p1+l+p1+l+p1+l+p4+l+p4+l+p4+l+p3+l+p3+l+p3+l+p2+l+p2+l+p2+"Z";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

Related

Change the stroke of d3 circles based on another dataset

I have build Leaflet based map visualization with circles on it made in D3. Based on the data selections, the map gets updated with values. Now, I am trying to change the stroke of circles based on another set of data. I mean if in my dataset there are 100 rows. After filtering, I show 60 circles and in these 60 points I have selected 30 points and I want to add the stroke to these 30 points and keep the rest of 60 points on map. Is it possible to do so?
.style("stroke", ....)
Can we call another dataset at this position?
This is my code
function updateSubset(filterLoad = 0) {
function applyLatLngToLayer(d) {
var y = d.geometry.coordinates[1]
var x = d.geometry.coordinates[0]
return map.latLngToLayerPoint(new L.LatLng(y, x))
}
var arr = geoData.features;
var filterObjArray = Object.entries(seldata_category);
console.log(filterObjArray)
var filterQuantArray = Object.entries(seldata_quant);
console.log(filterQuantArray)
var result = arr.filter(o => filterObjArray.every(([k,v]) => v.includes(o.properties[k])) && filterQuantArray.every(([k,[l,h]]) => o.properties[k] >= l && o.properties[k] <= h));
console.log(result);
console.log(seldata_category);
// handling size of the circles
var size_name = sizedropDown[current.size];
var size_extent = sizeExtents[current.size];
//console.log(colorExtents)
var sizeScale = d3.scaleSqrt()
.domain(size_extent)
.range([5,15]);
// handlimg color of the circles
var color_name = colordropDown[current.color];
var color_extent = colorExtents[current.color];
// console.log(color_extent)
var ordinalScale = d3.scaleOrdinal()
.domain(color_extent)
.range(c10(color_extent.length));
console.log(seldata_category)
console.log(seldata_quant)
// creating points using paths
var points = g.selectAll("circle")
.data(result);
var pointsEnter = points.enter().append("circle")
.attr("class", "points");
//console.log(points)
points.merge(pointsEnter).attr("r", function(d) { return sizeScale(d.properties[size_name]);})
.style("fill-opacity", 0.4)
.style("fill", function(d){ return ordinalScale(d.properties[color_name]);})
.on("mouseover",function(d){
var details = [];
for(var prop in d.properties){
details.push("<label>"+prop + " : </label>" + d.properties[prop]);
}
d3.select("#info_box").selectAll("li").data(details).enter().append("li").html(function(d){return d;});
$('#info_box li').addClass('list-group-item');
})
.on("mouseout", function(d){d3.select("#info_box").selectAll("li").remove();});
// map.on("viewreset", update);
//update();
map.on("viewreset", update);
update();
function update() {
var bounds = path.bounds(geoData);
topLeft = [bounds[0][0] + 10 , bounds[0][1] - 10]
bottomRight = [bounds[1][0] + 10 , bounds[1][1] + 10];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
var x = d3.selectAll('circle');
x.attr("transform",
function(d) {
return "translate(" +
applyLatLngToLayer(d).x + "," +
applyLatLngToLayer(d).y + ")";
});
}
points.exit().remove();
}

How to change the size of dots in beeswarm plots in D3.js

I've been looking at this example of a beeswarm plot in d3.js and I'm trying to figure out how to change the size of the dots and without getting the circles to overlap. It seems if the radius of the dots change, it doesn't take this into account when running the calculations of where to place the dots.
This is a cool visualization.
I've made a plunk of it here: https://plnkr.co/edit/VwyXfbc94oXp6kXQ7JFx?p=preview and modified it to work a bit more like you're looking for (I think). The real key is changing the call to handle collision to vary based on the radius of the circles (in the original post it's hard coded to 4, which works well when r === 3 but fails as r grows). The changes:
Make the circle radius into a variable (line 7 of script.js, var r = 3;)
Change the d3.forceCollide call to use that radius and a multiplier - line 110 (.force("collide", d3.forceCollide(r * 1.333)))
Change the .enter() call to use that radius as well (line 130: .attr("r", r))
This works reasonably well for reasonable values of r - but you'll need to adjust the height, and it might even be nice to just change the whole thing so that r is based on height (e.g. var r = height * .01). You'll notice that as is now, the circles go off the bottom and top of the graph area.
This post might be of interest as well: Conflict between d3.forceCollide() and d3.forceX/Y() with high strength() value
Here's the whole of script.js for posterity:
var w = 1000, h = 280;
var padding = [0, 40, 34, 40];
var r = 5;
var xScale = d3.scaleLinear()
.range([ padding[3], w - padding[1] ]);
var xAxis = d3.axisBottom(xScale)
.ticks(10, ".0s")
.tickSizeOuter(0);
var colors = d3.scaleOrdinal()
.domain(["asia", "africa", "northAmerica", "europe", "southAmerica", "oceania"])
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33']);
d3.select("#africaColor").style("color", colors("africa"));
d3.select("#namericaColor").style("color", colors("northAmerica"));
d3.select("#samericaColor").style("color", colors("southAmerica"));
d3.select("#asiaColor").style("color", colors("asia"));
d3.select("#europeColor").style("color", colors("europe"));
d3.select("#oceaniaColor").style("color", colors("oceania"));
var formatNumber = d3.format(",");
var tt = d3.select("#svganchor").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var svg = d3.select("#svganchor")
.append("svg")
.attr("width", w)
.attr("height", h);
var xline = svg.append("line")
.attr("stroke", "gray")
.attr("stroke-dasharray", "1,2");
var chartState = {};
chartState.variable = "totalEmission";
chartState.scale = "scaleLinear";
chartState.legend = "Total emissions, in kilotonnes";
d3.csv("co2bee.csv", function(error, data) {
if (error) throw error;
var dataSet = data;
xScale.domain(d3.extent(data, function(d) { return +d.totalEmission; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding[2]) + ")")
.call(xAxis);
var legend = svg.append("text")
.attr("text-anchor", "middle")
.attr("x", w / 2)
.attr("y", h - 4)
.attr("font-family", "PT Sans")
.attr("font-size", 12)
.attr("fill", "darkslategray")
.attr("fill-opacity", 1)
.attr("class", "legend");
redraw(chartState.variable);
d3.selectAll(".button1").on("click", function(){
var thisClicked = this.value;
chartState.variable = thisClicked;
if (thisClicked == "totalEmission"){
chartState.legend = "Total emissions, in kilotonnes";
}
if (thisClicked == "emissionPerCap"){
chartState.legend = "Per Capita emissions, in metric tons";
}
redraw(chartState.variable);
});
d3.selectAll(".button2").on("click", function(){
var thisClicked = this.value;
chartState.scale = thisClicked;
redraw(chartState.variable);
});
d3.selectAll("input").on("change", filter);
function redraw(variable){
if (chartState.scale == "scaleLinear"){ xScale = d3.scaleLinear().range([ padding[3], w - padding[1] ]);}
if (chartState.scale == "scaleLog"){ xScale = d3.scaleLog().range([ padding[3], w - padding[1] ]);}
xScale.domain(d3.extent(dataSet, function(d) { return +d[variable]; }));
var xAxis = d3.axisBottom(xScale)
.ticks(10, ".0s")
.tickSizeOuter(0);
d3.transition(svg).select(".x.axis").transition().duration(1000)
.call(xAxis);
var simulation = d3.forceSimulation(dataSet)
.force("x", d3.forceX(function(d) { return xScale(+d[variable]); }).strength(2))
.force("y", d3.forceY((h / 2)-padding[2]/2))
.force("collide", d3.forceCollide(r * 1.333))
.stop();
for (var i = 0; i < dataSet.length; ++i) simulation.tick();
var countriesCircles = svg.selectAll(".countries")
.data(dataSet, function(d) { return d.countryCode});
countriesCircles.exit()
.transition()
.duration(1000)
.attr("cx", 0)
.attr("cy", (h / 2)-padding[2]/2)
.remove();
countriesCircles.enter()
.append("circle")
.attr("class", "countries")
.attr("cx", 0)
.attr("cy", (h / 2)-padding[2]/2)
.attr("r", r)
.attr("fill", function(d){ return colors(d.continent)})
.merge(countriesCircles)
.transition()
.duration(2000)
.attr("cx", function(d) { console.log(d); return d.x; })
.attr("cy", function(d) { return d.y; });
legend.text(chartState.legend);
d3.selectAll(".countries").on("mousemove", function(d) {
tt.html("Country: <strong>" + d.countryName + "</strong><br>"
+ chartState.legend.slice(0, chartState.legend.indexOf(",")) + ": <strong>" + formatNumber(d[variable]) + "</strong>" + chartState.legend.slice(chartState.legend.lastIndexOf(" ")))
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.9);
xline.attr("x1", d3.select(this).attr("cx"))
.attr("y1", d3.select(this).attr("cy"))
.attr("y2", (h - padding[2]))
.attr("x2", d3.select(this).attr("cx"))
.attr("opacity", 1);
}).on("mouseout", function(d) {
tt.style("opacity", 0);
xline.attr("opacity", 0);
});
d3.selectAll(".x.axis, .legend").on("mousemove", function(){
tt.html("This axis uses SI prefixes:<br>m: 10<sup>-3</sup><br>k: 10<sup>3</sup><br>M: 10<sup>6</sup>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.9);
}).on("mouseout", function(d) {
tt.style("opacity", 0);
});
//end of redraw
}
function filter(){
function getCheckedBoxes(chkboxName) {
var checkboxes = document.getElementsByName(chkboxName);
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].defaultValue);
}
}
return checkboxesChecked.length > 0 ? checkboxesChecked : null;
}
var checkedBoxes = getCheckedBoxes("continent");
var newData = [];
if (checkedBoxes == null){
dataSet = newData;
redraw();
return;
};
for (var i = 0; i < checkedBoxes.length; i++){
var newArray = data.filter(function(d){
return d.continent == checkedBoxes[i];
});
Array.prototype.push.apply(newData, newArray);
}
dataSet = newData;
redraw(chartState.variable);
//end of filter
}
//end of d3.csv
});

how to delete stroke on one side of the svg in chart using d3

Hello i need to make chart like this
stroke on sides
lighter arc
no stroke between lighter and darker arc
Here is what i've got:
Codepen.io
HTML:
<div id="chart" style="width:400px; height: 400px; position:relative;"></div>
JavaScript:
var w = 400;
var h = 400;
var r = h/2;
var color = ["#27c794", "#fd7b74", "#fd9f9a"]
var color2 = ["#fd9f9a", "#27c794", "#fd7b74"]
var image_width = 32;
var image_height = 32;
var data = [
{"label": "Category A", "value":10, "icon": "http://files.gamebanana.com/img/ico/sprays/4f68c8d10306a.png"},
{"label": "Category B", "value":45, "icon": "http://files.gamebanana.com/img/ico/sprays/4f69c5f09c7bf.png"},
{"label": "Category C", "value":45, "icon": "http://files.gamebanana.com/img/ico/sprays/4ecb328ca104a.png"}
];
var vis = d3.select('#chart').append("svg:svg").data([data]).attr("width", w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.pie().value(function(d){return d.value;});
// declare an arc generator function
var arc = d3.arc().outerRadius(r -20).innerRadius(r - 100);
var arc2 = d3.arc().outerRadius(r -10).innerRadius(r - 20);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i){
return color[i];
}).attr("stroke", "#fff").style("stroke-width", "3")
.attr("d", function (d) {
return arc(d);
})
arcs.append("svg:path").attr("d", function (d) {
return arc2(d);
}).attr("fill", function(d, i){
return color[i];
}).style("opacity", "0.4");
var x = d3.select("#chart").append("div");
x.append("p").text("1212");
x.append("p").text("11");
x.selectAll("p").attr('style', function(d, i) {
return "color:" + color[i];
})
// add the image
arcs.append("svg:image").attr("transform", function(d, i){
var x = arc.centroid(d)[0] - image_width/2;
var y = arc.centroid(d)[1] - image_height/2;
return "translate(" + x + "," + y + ")";
})
.attr("xlink:href",function(d) { if(d.data.value >= 15) {return d.data.icon;}})
.attr("width", image_width)
.attr("height", image_height);

D3.js Radar chart line drawing

I am trying to create a radar chart similar to the link here (
http://www.larsko.org/v/euc/).
I was able to create axes (my work so far), but I am having a problem to draw lines in it.
For instance, if I have a list of values something like below, how can I draw a line in the radar chart?
var tempData = [56784, 5.898, 3417, 0, 0, 0]
Edit: I have included code. I am having a problem finding XY coordinates and I think XY value has to be derived from "scales".
var width = 1000,
height = 960,
r = (960 / 2) - 160;
var svg = d3.select("#radar")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + ", " + height / 2 + ")");
d3.csv("data/results.csv", function(data) {
var headerNames = d3.keys(data[0]);
headerNames.splice(0, 1); //Remove 'scenario'
var minList = $.map(headerNames, function(h) {
return d3.min($.map(data, function(d) {
return d[h];
}));
}),
maxList = $.map(headerNames, function(h) {
return d3.max($.map(data, function(d) {
return d[h];
}));
}),
scales = $.map(headerNames, function(h, i) {
return d3.scale.linear()
.domain([minList[i], maxList[i]])
.range([50, r]);
}),
axes = $.map(headerNames, function(h, i) {
return d3.svg.axis()
.scale(scales[i])
.tickSize(4);
});
function angle(i) {
return i * (2 * Math.PI / headerNames.length) + Math.PI / headerNames.length;
}
var line = d3.svg.line()
.interpolate("cardinal-closed")
/* computing X and Y: I am having a problem here
.x(function(d){ return scales(d); })
.y(function(d){ return scales(d); }); */
$.each(axes, function(i, a) {
svg.append("g")
.attr("transform", "rotate(" + Math.round(angle(i) * (180 / Math.PI)) + ")")
.call(a)
.selectAll("text")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + -angle(i) * (180 / Math.PI) + ")";
})
//Drawing line
svg.selectAll(".layer")
.data(data)
.enter()
.append("path")
.attr("class", "layer")
.attr("d", function(d) {
return line(d);
})
}) // End CSV
Example results.csv
scenario,n_dead_oaks,percent_dead_oaks,infected_area_ha,money_spent,area_treated_ha,price_per_oak
baseline,56784,5.898,3417,0,0,0
scen2,52725,5.477,3294,382036,35,94.12071939
RS_1,58037,6.028,3407,796705,59,-635.8379888
RS_2,33571,3.487,2555,1841047,104,79.31103261
RS_3,46111,4.79,2762,1176461,61,110.227771
As Squeegy suggested, you should share some code showing your current progress and how you have achieved to create the axes.
Anyways, this is how I would go about this:
For a given list of values that you want to represent as a line, find the [x,y] coordinates of every point of the line, i.e. place your data-points on each axis. If you have a scale system in place already to draw your axes, this shouldn't be too hard.
Use d3.svg.line to draw a line that goes through all these points.
The code would end up looking like this:
var tempData = [56784, 5.898, 3417, 0, 0, 0];
/** compute tempPoints from tempData **/
var tempPoints = [[123, 30], [12, 123], [123, 123], [0,0], [0,0], [0,0]];
var line = d3.svg.line();
d3.select('svg').append('path').attr('d', line(tempPoints) + 'Z'); // the trailing Z closes the path
I think I have a solution for now and I appreciate all of your response! Here is my current solution for my posting.
function getRowValues(data) {
return $.map(data, function(d, i) {
if (i != "scenario") {
return d;
}
});
}
function getCoor(data) {
console.log(data);
var row = getRowValues(data),
x,
y,
coor = [];
for (var i = 0; i < row.length; i++) {
x = Math.round(Math.cos(angle(i)) * scales[i](row[i]));
y = Math.round(Math.sin(angle(i)) * scales[i](row[i]));
coor.push([x, y]);
}
return coor;
}
var line = d3.svg.line()
.interpolate("cardinal-closed")
.tension(0.85);
svg.selectAll(".layer")
.data(data)
.enter()
.append("path")
.attr("class", "layer")
.attr("d", function(d) { return line(getCoor(d)) + "Z"; })
.style("stroke", function(d, i){ return colors[i]; })
.style("fill", "none");

Not able to add the fill between the paths. using d3js

i am trying to fill the background between 2 lines, but i am not getting any correct output.
and i would like to remove the tick line in the y axis as well. how to get this both?
here is my code : any one correct me please?
$(function(){
var m = [80, 80, 80, 80]; // margins
var w = 300; // width
var h = 450; // height
var plan = 55;
var actual = 38;
var variation = plan - actual;
var data = [0,plan];
var data1 = [0,actual];
var x = d3.scale.linear().domain([0, 2]).range([0, w]);
var y = d3.scale.linear().domain([0, 100]).range([h, 0]);
var line = d3.svg.line()
.x(function(d,i) {
return x(i);
})
.y(function(d) {
return y(d);
})
var graph = d3.select("#graph").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
var yAxisLeft = d3.svg.axis().scale(y).tickSize(-w).orient("left");
graph.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(-0,0)")
.call(yAxisLeft);
graph.append("svg:path").attr("d", line(data));
graph.append("svg:path").attr("d", line(data1));
//not able to fill the bg between 2 lines
var area = d3.svg.area()
.x(function(d, i) { return 0 })
.x1(function(d, i) { return plan })
.y0(function(d, i) { return y(actual); })
.y1(function(d, i) { return y(variation); })
.interpolate("basis");
graph.append("path")
.datum(data)
.attr("d", area)
.attr("fill", "#CCC");
});
Live Demo
Concerning your area :
var area = d3.svg.area()
.x(function(d, i) { return 0 })
.x1(function(d, i) { return plan })
.y0(function(d, i) { return y(actual); })
.y1(function(d, i) { return y(variation); })
.interpolate("basis");
x and x1 are returning static values, so it won't draw an area but just a line
your both lines have same x axis so you just have to specify .x()
y0 and y1 are also returnin static values
Here is a working version :
var area = d3.svg.area()
.x(function(d, i) { return x(i) })
.y0(function(d, i) { return y(data[i]); })
.y1(function(d, i) { return y(data1[i]); })
Also be careful, you have a fill: none; in your css file so you won't see anything.
Functional plunker : http://plnkr.co/edit/xFNF3BQzd0IO5bauAiFU?p=preview

Resources