Using dispatch events in d3.js

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
In this Plunker (Will post all code below) I'm using d3.dispatch to handle all updates of the charts.
What I want to know is if I'm using the dispatch events properly or as it was "intended" to be used, I could not find many examples using this particular method which is why I'm unsure of whether I'm using it correctly. Or just general code review.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v5.min.js"></script>
<style>
/* --- Global --- */
body
margin:auto;
width: 850px;
font: 10px arial;
padding: 25px;
color:#333;
/* --- Chart --- */
.grid--y path,
.grid--y text
display: none;
.grid--y line
opacity:.15;
.axis--xS path
display: none;
.grid--yS path,
.grid--yS text
display: none;
.grid--yS line
opacity:.15;
</style>
</head>
<body>
<!-- Selection Category -->
<b>Välj Uteblivande</b>
<select id="category" class="options">
<option value="AA">All</option>
<option value="1U">1 Unit</option>
</select>
<!-- Selection New dataset -->
<span style="margin-left:30px;">
<b>Choose Year</b>
<select id="year" class="options">
<option value="2017">2017</option>
<option value="2016">2016</option>
</select>
</span>
<!-- Chart -->
<div id="chart"></div>
<script>
var durations = 0,
formatValue = d3.format(".2s"),
formatPercent = d3.format(",.0f");
var teamColor = d3.scaleOrdinal()
.range(["steelblue","darkorange", "lightblue"]);
var dispatch = d3.dispatch("load", "update");
var files = ["data1.csv", "data2.csv"];
Promise.all(files.map(url => d3.csv(url))).then(function(dataSet)
// === Load all data ===
var data1 = dataSet[0];
var data2 = dataSet[1];
dispatch.call("load", this);
// === Update data ===
update();
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
dispatch.call("update", this, data, input);
// === Event handler ===
d3.selectAll(".options").on("change", function()
durations = 750;
update();
)
);
dispatch.on("load.occupation", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 540 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
let x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
let x1 = d3.scaleBand();
let y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y).ticks(null, "s");
yGrid = d3.axisLeft(y).tickSize(-width);
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
g.append("g")
.attr("class", "axis grid--y");
dispatch.on("update.occupation", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
let copy = ;
keys.forEach(function(t)
t = t.slice(0, -2)
copy.push(t)
)
var data = dataInit.filter(function(d)
return d.State !== "ALL"
)
let sumOfTeam = d3.sum(data, d=> d3.sum(keys, key=> d[key]));
y.domain([0, d3.max(data, function(d)
return d3.max(keys, function(key)
return d.sliceTotal;
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(durations)
.call(yGrid);
// bars
let barGroups = g.selectAll("g.layer").data(data);
barGroups.enter().append("g")
.classed('layer', true);
barGroups.exit().remove();
// bars
let barGroups2 = g.selectAll("g.layer2").data(data);
barGroups2.enter().append("g")
.classed('layer2', true);
barGroups2.exit().remove();
// xDomains
x0.domain(data.map(function(d) return d.State; ));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
// Update axis
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
g.selectAll("g.layer").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let bars = g.selectAll("g.layer").selectAll(".bars")
.data(function(d)
return copy.map(function(key) // Return copy
return key: key+input, value: d[key+input] ; // Add input
);
);
bars = bars
.enter()
.append("rect")
.attr("class", "bars")
.attr("fill", function(d) return teamColor(d.key); )
.attr("width", x1.bandwidth())
.attr("x", function(d) return x1(d.key); )
.merge(bars)
bars.transition().duration(durations)
.attr("y", function(d) return y(d["value"]); )
.attr("height", function(d) return height - y(d["value"]); );
bars.exit().remove();
g.selectAll("g.layer2").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let barsTotal = g.selectAll(".test")
.data(data, function(d)
return d.State;
);
barsTotal = barsTotal
.enter()
.append("rect")
.attr("fill", "none")
.attr("stroke","#999")
.attr("stroke-width","1px")
.attr("class","test")
.attr("width", x0.bandwidth())
.merge(barsTotal);
barsTotal.transition().duration(durations)
.attr("x", function(d)
return x0(d.State);
)
.attr("y", function(d)
return y(d.sliceTotal);
)
.attr("height", function(d)
return height - y(d.sliceTotal);
);
barsTotal.exit().remove();
);
);
dispatch.on("load.all", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 150 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(null, "s");
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
dispatch.on("update.all", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
var copy =
keys.forEach(function(s)
s = s.slice(0, -2)
copy.push(s)
)
let combined = ;
copy.forEach(function(t)
t = t + input
combined.push(t)
)
var data = dataInit.filter(function(d)
return d.State == "ALL"
)
y.domain([0, d3.max(data, function(d)
return d3.sum(copy, function(key)
return d[key + input];
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
teamColor.domain(combined);
x.domain(data.map(function(d) return d.State; ));
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
var stacking = d3.stack().keys(combined)(data)
let barGroups = g.selectAll("g.layer")
.data(stacking,function(d)
return d.key.split(' ')[0] + d.key.split(' ')[1]
);
barGroups
.enter()
.append("g")
.classed('layer', true);
barGroups.exit().remove();
g.selectAll("g.layer").transition().duration(durations)
.attr("fill", function(d) return teamColor(d.key); );
let bars = g.selectAll("g.layer").selectAll("rect")
.data(function(d) return d; , d => d.data.State);
bars = bars
.enter()
.append("rect")
.attr("width", x.bandwidth())
.merge(bars);
bars.transition().duration(durations)
.attr("x", function(d) return x(d.data.State); )
.attr("y", function(d) return y(d[1]); )
.attr("height", function(d) return y(d[0]) - y(d[1]); );
);
);
</script>
</body>
javascript d3.js
add a comment |Â
up vote
1
down vote
favorite
In this Plunker (Will post all code below) I'm using d3.dispatch to handle all updates of the charts.
What I want to know is if I'm using the dispatch events properly or as it was "intended" to be used, I could not find many examples using this particular method which is why I'm unsure of whether I'm using it correctly. Or just general code review.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v5.min.js"></script>
<style>
/* --- Global --- */
body
margin:auto;
width: 850px;
font: 10px arial;
padding: 25px;
color:#333;
/* --- Chart --- */
.grid--y path,
.grid--y text
display: none;
.grid--y line
opacity:.15;
.axis--xS path
display: none;
.grid--yS path,
.grid--yS text
display: none;
.grid--yS line
opacity:.15;
</style>
</head>
<body>
<!-- Selection Category -->
<b>Välj Uteblivande</b>
<select id="category" class="options">
<option value="AA">All</option>
<option value="1U">1 Unit</option>
</select>
<!-- Selection New dataset -->
<span style="margin-left:30px;">
<b>Choose Year</b>
<select id="year" class="options">
<option value="2017">2017</option>
<option value="2016">2016</option>
</select>
</span>
<!-- Chart -->
<div id="chart"></div>
<script>
var durations = 0,
formatValue = d3.format(".2s"),
formatPercent = d3.format(",.0f");
var teamColor = d3.scaleOrdinal()
.range(["steelblue","darkorange", "lightblue"]);
var dispatch = d3.dispatch("load", "update");
var files = ["data1.csv", "data2.csv"];
Promise.all(files.map(url => d3.csv(url))).then(function(dataSet)
// === Load all data ===
var data1 = dataSet[0];
var data2 = dataSet[1];
dispatch.call("load", this);
// === Update data ===
update();
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
dispatch.call("update", this, data, input);
// === Event handler ===
d3.selectAll(".options").on("change", function()
durations = 750;
update();
)
);
dispatch.on("load.occupation", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 540 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
let x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
let x1 = d3.scaleBand();
let y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y).ticks(null, "s");
yGrid = d3.axisLeft(y).tickSize(-width);
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
g.append("g")
.attr("class", "axis grid--y");
dispatch.on("update.occupation", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
let copy = ;
keys.forEach(function(t)
t = t.slice(0, -2)
copy.push(t)
)
var data = dataInit.filter(function(d)
return d.State !== "ALL"
)
let sumOfTeam = d3.sum(data, d=> d3.sum(keys, key=> d[key]));
y.domain([0, d3.max(data, function(d)
return d3.max(keys, function(key)
return d.sliceTotal;
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(durations)
.call(yGrid);
// bars
let barGroups = g.selectAll("g.layer").data(data);
barGroups.enter().append("g")
.classed('layer', true);
barGroups.exit().remove();
// bars
let barGroups2 = g.selectAll("g.layer2").data(data);
barGroups2.enter().append("g")
.classed('layer2', true);
barGroups2.exit().remove();
// xDomains
x0.domain(data.map(function(d) return d.State; ));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
// Update axis
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
g.selectAll("g.layer").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let bars = g.selectAll("g.layer").selectAll(".bars")
.data(function(d)
return copy.map(function(key) // Return copy
return key: key+input, value: d[key+input] ; // Add input
);
);
bars = bars
.enter()
.append("rect")
.attr("class", "bars")
.attr("fill", function(d) return teamColor(d.key); )
.attr("width", x1.bandwidth())
.attr("x", function(d) return x1(d.key); )
.merge(bars)
bars.transition().duration(durations)
.attr("y", function(d) return y(d["value"]); )
.attr("height", function(d) return height - y(d["value"]); );
bars.exit().remove();
g.selectAll("g.layer2").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let barsTotal = g.selectAll(".test")
.data(data, function(d)
return d.State;
);
barsTotal = barsTotal
.enter()
.append("rect")
.attr("fill", "none")
.attr("stroke","#999")
.attr("stroke-width","1px")
.attr("class","test")
.attr("width", x0.bandwidth())
.merge(barsTotal);
barsTotal.transition().duration(durations)
.attr("x", function(d)
return x0(d.State);
)
.attr("y", function(d)
return y(d.sliceTotal);
)
.attr("height", function(d)
return height - y(d.sliceTotal);
);
barsTotal.exit().remove();
);
);
dispatch.on("load.all", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 150 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(null, "s");
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
dispatch.on("update.all", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
var copy =
keys.forEach(function(s)
s = s.slice(0, -2)
copy.push(s)
)
let combined = ;
copy.forEach(function(t)
t = t + input
combined.push(t)
)
var data = dataInit.filter(function(d)
return d.State == "ALL"
)
y.domain([0, d3.max(data, function(d)
return d3.sum(copy, function(key)
return d[key + input];
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
teamColor.domain(combined);
x.domain(data.map(function(d) return d.State; ));
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
var stacking = d3.stack().keys(combined)(data)
let barGroups = g.selectAll("g.layer")
.data(stacking,function(d)
return d.key.split(' ')[0] + d.key.split(' ')[1]
);
barGroups
.enter()
.append("g")
.classed('layer', true);
barGroups.exit().remove();
g.selectAll("g.layer").transition().duration(durations)
.attr("fill", function(d) return teamColor(d.key); );
let bars = g.selectAll("g.layer").selectAll("rect")
.data(function(d) return d; , d => d.data.State);
bars = bars
.enter()
.append("rect")
.attr("width", x.bandwidth())
.merge(bars);
bars.transition().duration(durations)
.attr("x", function(d) return x(d.data.State); )
.attr("y", function(d) return y(d[1]); )
.attr("height", function(d) return y(d[0]) - y(d[1]); );
);
);
</script>
</body>
javascript d3.js
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
In this Plunker (Will post all code below) I'm using d3.dispatch to handle all updates of the charts.
What I want to know is if I'm using the dispatch events properly or as it was "intended" to be used, I could not find many examples using this particular method which is why I'm unsure of whether I'm using it correctly. Or just general code review.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v5.min.js"></script>
<style>
/* --- Global --- */
body
margin:auto;
width: 850px;
font: 10px arial;
padding: 25px;
color:#333;
/* --- Chart --- */
.grid--y path,
.grid--y text
display: none;
.grid--y line
opacity:.15;
.axis--xS path
display: none;
.grid--yS path,
.grid--yS text
display: none;
.grid--yS line
opacity:.15;
</style>
</head>
<body>
<!-- Selection Category -->
<b>Välj Uteblivande</b>
<select id="category" class="options">
<option value="AA">All</option>
<option value="1U">1 Unit</option>
</select>
<!-- Selection New dataset -->
<span style="margin-left:30px;">
<b>Choose Year</b>
<select id="year" class="options">
<option value="2017">2017</option>
<option value="2016">2016</option>
</select>
</span>
<!-- Chart -->
<div id="chart"></div>
<script>
var durations = 0,
formatValue = d3.format(".2s"),
formatPercent = d3.format(",.0f");
var teamColor = d3.scaleOrdinal()
.range(["steelblue","darkorange", "lightblue"]);
var dispatch = d3.dispatch("load", "update");
var files = ["data1.csv", "data2.csv"];
Promise.all(files.map(url => d3.csv(url))).then(function(dataSet)
// === Load all data ===
var data1 = dataSet[0];
var data2 = dataSet[1];
dispatch.call("load", this);
// === Update data ===
update();
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
dispatch.call("update", this, data, input);
// === Event handler ===
d3.selectAll(".options").on("change", function()
durations = 750;
update();
)
);
dispatch.on("load.occupation", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 540 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
let x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
let x1 = d3.scaleBand();
let y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y).ticks(null, "s");
yGrid = d3.axisLeft(y).tickSize(-width);
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
g.append("g")
.attr("class", "axis grid--y");
dispatch.on("update.occupation", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
let copy = ;
keys.forEach(function(t)
t = t.slice(0, -2)
copy.push(t)
)
var data = dataInit.filter(function(d)
return d.State !== "ALL"
)
let sumOfTeam = d3.sum(data, d=> d3.sum(keys, key=> d[key]));
y.domain([0, d3.max(data, function(d)
return d3.max(keys, function(key)
return d.sliceTotal;
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(durations)
.call(yGrid);
// bars
let barGroups = g.selectAll("g.layer").data(data);
barGroups.enter().append("g")
.classed('layer', true);
barGroups.exit().remove();
// bars
let barGroups2 = g.selectAll("g.layer2").data(data);
barGroups2.enter().append("g")
.classed('layer2', true);
barGroups2.exit().remove();
// xDomains
x0.domain(data.map(function(d) return d.State; ));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
// Update axis
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
g.selectAll("g.layer").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let bars = g.selectAll("g.layer").selectAll(".bars")
.data(function(d)
return copy.map(function(key) // Return copy
return key: key+input, value: d[key+input] ; // Add input
);
);
bars = bars
.enter()
.append("rect")
.attr("class", "bars")
.attr("fill", function(d) return teamColor(d.key); )
.attr("width", x1.bandwidth())
.attr("x", function(d) return x1(d.key); )
.merge(bars)
bars.transition().duration(durations)
.attr("y", function(d) return y(d["value"]); )
.attr("height", function(d) return height - y(d["value"]); );
bars.exit().remove();
g.selectAll("g.layer2").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let barsTotal = g.selectAll(".test")
.data(data, function(d)
return d.State;
);
barsTotal = barsTotal
.enter()
.append("rect")
.attr("fill", "none")
.attr("stroke","#999")
.attr("stroke-width","1px")
.attr("class","test")
.attr("width", x0.bandwidth())
.merge(barsTotal);
barsTotal.transition().duration(durations)
.attr("x", function(d)
return x0(d.State);
)
.attr("y", function(d)
return y(d.sliceTotal);
)
.attr("height", function(d)
return height - y(d.sliceTotal);
);
barsTotal.exit().remove();
);
);
dispatch.on("load.all", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 150 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(null, "s");
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
dispatch.on("update.all", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
var copy =
keys.forEach(function(s)
s = s.slice(0, -2)
copy.push(s)
)
let combined = ;
copy.forEach(function(t)
t = t + input
combined.push(t)
)
var data = dataInit.filter(function(d)
return d.State == "ALL"
)
y.domain([0, d3.max(data, function(d)
return d3.sum(copy, function(key)
return d[key + input];
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
teamColor.domain(combined);
x.domain(data.map(function(d) return d.State; ));
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
var stacking = d3.stack().keys(combined)(data)
let barGroups = g.selectAll("g.layer")
.data(stacking,function(d)
return d.key.split(' ')[0] + d.key.split(' ')[1]
);
barGroups
.enter()
.append("g")
.classed('layer', true);
barGroups.exit().remove();
g.selectAll("g.layer").transition().duration(durations)
.attr("fill", function(d) return teamColor(d.key); );
let bars = g.selectAll("g.layer").selectAll("rect")
.data(function(d) return d; , d => d.data.State);
bars = bars
.enter()
.append("rect")
.attr("width", x.bandwidth())
.merge(bars);
bars.transition().duration(durations)
.attr("x", function(d) return x(d.data.State); )
.attr("y", function(d) return y(d[1]); )
.attr("height", function(d) return y(d[0]) - y(d[1]); );
);
);
</script>
</body>
javascript d3.js
In this Plunker (Will post all code below) I'm using d3.dispatch to handle all updates of the charts.
What I want to know is if I'm using the dispatch events properly or as it was "intended" to be used, I could not find many examples using this particular method which is why I'm unsure of whether I'm using it correctly. Or just general code review.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v5.min.js"></script>
<style>
/* --- Global --- */
body
margin:auto;
width: 850px;
font: 10px arial;
padding: 25px;
color:#333;
/* --- Chart --- */
.grid--y path,
.grid--y text
display: none;
.grid--y line
opacity:.15;
.axis--xS path
display: none;
.grid--yS path,
.grid--yS text
display: none;
.grid--yS line
opacity:.15;
</style>
</head>
<body>
<!-- Selection Category -->
<b>Välj Uteblivande</b>
<select id="category" class="options">
<option value="AA">All</option>
<option value="1U">1 Unit</option>
</select>
<!-- Selection New dataset -->
<span style="margin-left:30px;">
<b>Choose Year</b>
<select id="year" class="options">
<option value="2017">2017</option>
<option value="2016">2016</option>
</select>
</span>
<!-- Chart -->
<div id="chart"></div>
<script>
var durations = 0,
formatValue = d3.format(".2s"),
formatPercent = d3.format(",.0f");
var teamColor = d3.scaleOrdinal()
.range(["steelblue","darkorange", "lightblue"]);
var dispatch = d3.dispatch("load", "update");
var files = ["data1.csv", "data2.csv"];
Promise.all(files.map(url => d3.csv(url))).then(function(dataSet)
// === Load all data ===
var data1 = dataSet[0];
var data2 = dataSet[1];
dispatch.call("load", this);
// === Update data ===
update();
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
dispatch.call("update", this, data, input);
// === Event handler ===
d3.selectAll(".options").on("change", function()
durations = 750;
update();
)
);
dispatch.on("load.occupation", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 540 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
let x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
let x1 = d3.scaleBand();
let y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x0),
yAxis = d3.axisLeft(y).ticks(null, "s");
yGrid = d3.axisLeft(y).tickSize(-width);
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
g.append("g")
.attr("class", "axis grid--y");
dispatch.on("update.occupation", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
let copy = ;
keys.forEach(function(t)
t = t.slice(0, -2)
copy.push(t)
)
var data = dataInit.filter(function(d)
return d.State !== "ALL"
)
let sumOfTeam = d3.sum(data, d=> d3.sum(keys, key=> d[key]));
y.domain([0, d3.max(data, function(d)
return d3.max(keys, function(key)
return d.sliceTotal;
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(durations)
.call(yGrid);
// bars
let barGroups = g.selectAll("g.layer").data(data);
barGroups.enter().append("g")
.classed('layer', true);
barGroups.exit().remove();
// bars
let barGroups2 = g.selectAll("g.layer2").data(data);
barGroups2.enter().append("g")
.classed('layer2', true);
barGroups2.exit().remove();
// xDomains
x0.domain(data.map(function(d) return d.State; ));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
// Update axis
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
g.selectAll("g.layer").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let bars = g.selectAll("g.layer").selectAll(".bars")
.data(function(d)
return copy.map(function(key) // Return copy
return key: key+input, value: d[key+input] ; // Add input
);
);
bars = bars
.enter()
.append("rect")
.attr("class", "bars")
.attr("fill", function(d) return teamColor(d.key); )
.attr("width", x1.bandwidth())
.attr("x", function(d) return x1(d.key); )
.merge(bars)
bars.transition().duration(durations)
.attr("y", function(d) return y(d["value"]); )
.attr("height", function(d) return height - y(d["value"]); );
bars.exit().remove();
g.selectAll("g.layer2").transition().duration(durations)
.attr("transform", function(d, i)
return "translate(" + x0(d.State) + ",0)";
);
let barsTotal = g.selectAll(".test")
.data(data, function(d)
return d.State;
);
barsTotal = barsTotal
.enter()
.append("rect")
.attr("fill", "none")
.attr("stroke","#999")
.attr("stroke-width","1px")
.attr("class","test")
.attr("width", x0.bandwidth())
.merge(barsTotal);
barsTotal.transition().duration(durations)
.attr("x", function(d)
return x0(d.State);
)
.attr("y", function(d)
return y(d.sliceTotal);
)
.attr("height", function(d)
return height - y(d.sliceTotal);
);
barsTotal.exit().remove();
);
);
dispatch.on("load.all", function()
let margin = top: 35, right: 45, bottom: 35, left: 45,
width = 150 - margin.left - margin.right,
height = 420 - margin.top - margin.bottom;
let g = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
let xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(null, "s");
g.append("g")
.attr("class","axis axis--x")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "axis axis--y");
dispatch.on("update.all", function(dataInit, input)
var keys = dataInit.columns.slice(1, 4);
var copy =
keys.forEach(function(s)
s = s.slice(0, -2)
copy.push(s)
)
let combined = ;
copy.forEach(function(t)
t = t + input
combined.push(t)
)
var data = dataInit.filter(function(d)
return d.State == "ALL"
)
y.domain([0, d3.max(data, function(d)
return d3.sum(copy, function(key)
return d[key + input];
);
)
]).nice();
g.selectAll(".axis.axis--y").transition()
.duration(durations)
.call(yAxis);
teamColor.domain(combined);
x.domain(data.map(function(d) return d.State; ));
g.selectAll(".axis.axis--x").transition()
.duration(durations)
.call(xAxis);
var stacking = d3.stack().keys(combined)(data)
let barGroups = g.selectAll("g.layer")
.data(stacking,function(d)
return d.key.split(' ')[0] + d.key.split(' ')[1]
);
barGroups
.enter()
.append("g")
.classed('layer', true);
barGroups.exit().remove();
g.selectAll("g.layer").transition().duration(durations)
.attr("fill", function(d) return teamColor(d.key); );
let bars = g.selectAll("g.layer").selectAll("rect")
.data(function(d) return d; , d => d.data.State);
bars = bars
.enter()
.append("rect")
.attr("width", x.bandwidth())
.merge(bars);
bars.transition().duration(durations)
.attr("x", function(d) return x(d.data.State); )
.attr("y", function(d) return y(d[1]); )
.attr("height", function(d) return y(d[0]) - y(d[1]); );
);
);
</script>
</body>
javascript d3.js
asked Apr 7 at 14:03
Robert Andersson
1667
1667
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
While you can do it using d3.dispatch, as you just found out, it doesn't seem to be a good practice:
It's not necessary, you can do the same using regular functions (see below). The main reason why it is not needed is that, despite the fact that you have two events,
loadandupdate, you just need one, since you already have the necessary enter, update and exit selections, and their proper transitions.It's not idiomatic and adds needless complications to your code. Because it's not idiomatic, it will make harder to other D3 programmers trying to understand/maintain your code in the future.
It creates a lot of duplications (in my solution below I saved you 100 lines of code, going from 379 to 279 lines).
That being said, my proposed solution is: use regular functions.
So, in your code, I put all the drawing parts inside a draw function...
function draw(data, input) {
... which is called by the update:
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
draw(data, input)
After that, I removed all the parts that don't change and that you need to paint just once from draw, like the SVG and the axes selections.
Last but not least, good on you for using the brand new D3 v5 and Promise.all!
Here is the updated Plunker: https://plnkr.co/edit/t17PoC4dl2aSOhn2Dxz6?p=preview
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outerdrawnand an innerupdateor whatever name you want. So the variables are confined insidedraw.
â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
While you can do it using d3.dispatch, as you just found out, it doesn't seem to be a good practice:
It's not necessary, you can do the same using regular functions (see below). The main reason why it is not needed is that, despite the fact that you have two events,
loadandupdate, you just need one, since you already have the necessary enter, update and exit selections, and their proper transitions.It's not idiomatic and adds needless complications to your code. Because it's not idiomatic, it will make harder to other D3 programmers trying to understand/maintain your code in the future.
It creates a lot of duplications (in my solution below I saved you 100 lines of code, going from 379 to 279 lines).
That being said, my proposed solution is: use regular functions.
So, in your code, I put all the drawing parts inside a draw function...
function draw(data, input) {
... which is called by the update:
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
draw(data, input)
After that, I removed all the parts that don't change and that you need to paint just once from draw, like the SVG and the axes selections.
Last but not least, good on you for using the brand new D3 v5 and Promise.all!
Here is the updated Plunker: https://plnkr.co/edit/t17PoC4dl2aSOhn2Dxz6?p=preview
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outerdrawnand an innerupdateor whatever name you want. So the variables are confined insidedraw.
â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
add a comment |Â
up vote
1
down vote
accepted
While you can do it using d3.dispatch, as you just found out, it doesn't seem to be a good practice:
It's not necessary, you can do the same using regular functions (see below). The main reason why it is not needed is that, despite the fact that you have two events,
loadandupdate, you just need one, since you already have the necessary enter, update and exit selections, and their proper transitions.It's not idiomatic and adds needless complications to your code. Because it's not idiomatic, it will make harder to other D3 programmers trying to understand/maintain your code in the future.
It creates a lot of duplications (in my solution below I saved you 100 lines of code, going from 379 to 279 lines).
That being said, my proposed solution is: use regular functions.
So, in your code, I put all the drawing parts inside a draw function...
function draw(data, input) {
... which is called by the update:
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
draw(data, input)
After that, I removed all the parts that don't change and that you need to paint just once from draw, like the SVG and the axes selections.
Last but not least, good on you for using the brand new D3 v5 and Promise.all!
Here is the updated Plunker: https://plnkr.co/edit/t17PoC4dl2aSOhn2Dxz6?p=preview
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outerdrawnand an innerupdateor whatever name you want. So the variables are confined insidedraw.
â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
add a comment |Â
up vote
1
down vote
accepted
up vote
1
down vote
accepted
While you can do it using d3.dispatch, as you just found out, it doesn't seem to be a good practice:
It's not necessary, you can do the same using regular functions (see below). The main reason why it is not needed is that, despite the fact that you have two events,
loadandupdate, you just need one, since you already have the necessary enter, update and exit selections, and their proper transitions.It's not idiomatic and adds needless complications to your code. Because it's not idiomatic, it will make harder to other D3 programmers trying to understand/maintain your code in the future.
It creates a lot of duplications (in my solution below I saved you 100 lines of code, going from 379 to 279 lines).
That being said, my proposed solution is: use regular functions.
So, in your code, I put all the drawing parts inside a draw function...
function draw(data, input) {
... which is called by the update:
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
draw(data, input)
After that, I removed all the parts that don't change and that you need to paint just once from draw, like the SVG and the axes selections.
Last but not least, good on you for using the brand new D3 v5 and Promise.all!
Here is the updated Plunker: https://plnkr.co/edit/t17PoC4dl2aSOhn2Dxz6?p=preview
While you can do it using d3.dispatch, as you just found out, it doesn't seem to be a good practice:
It's not necessary, you can do the same using regular functions (see below). The main reason why it is not needed is that, despite the fact that you have two events,
loadandupdate, you just need one, since you already have the necessary enter, update and exit selections, and their proper transitions.It's not idiomatic and adds needless complications to your code. Because it's not idiomatic, it will make harder to other D3 programmers trying to understand/maintain your code in the future.
It creates a lot of duplications (in my solution below I saved you 100 lines of code, going from 379 to 279 lines).
That being said, my proposed solution is: use regular functions.
So, in your code, I put all the drawing parts inside a draw function...
function draw(data, input) {
... which is called by the update:
function update()
var input = d3.select('#category')
.property('value');
var data = d3.select('#year')
.property('value') == '2017' ? data1 : data2;
data.forEach(function(d, i, columns)
for (var i = 1, ttl = 0, n = columns.length; i < n; ++i)
ttl += d[columns[i]] = +d[columns[i]];
d.total = ttl;
d.sliceTotal = d3.sum([
d["Team 1 " + input],
d["Team 2 " + input],
d["Team 3 " + input]
]);
return d;
)
draw(data, input)
After that, I removed all the parts that don't change and that you need to paint just once from draw, like the SVG and the axes selections.
Last but not least, good on you for using the brand new D3 v5 and Promise.all!
Here is the updated Plunker: https://plnkr.co/edit/t17PoC4dl2aSOhn2Dxz6?p=preview
edited Apr 8 at 8:33
answered Apr 8 at 1:30
Gerardo Furtado
1,1342420
1,1342420
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outerdrawnand an innerupdateor whatever name you want. So the variables are confined insidedraw.
â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
add a comment |Â
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outerdrawnand an innerupdateor whatever name you want. So the variables are confined insidedraw.
â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
Thanks a lot! Although I like your solution, the reason I had my code setup like that (having margin, width etc not being global) is so that if I wanted to add another chart I wouldn't have to worry as much about naming variables, that might be a bad reason though.
â Robert Andersson
Apr 8 at 8:49
In that case nest two functions, and outer
drawn and an inner update or whatever name you want. So the variables are confined inside draw.â Gerardo Furtado
Apr 8 at 8:52
In that case nest two functions, and outer
drawn and an inner update or whatever name you want. So the variables are confined inside draw.â Gerardo Furtado
Apr 8 at 8:52
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
You don't happen to have an example of this? Because I tried doing it with functions at first but was unable to call the nested function from my event handler without also calling the function surrounding the update function
â Robert Andersson
Apr 8 at 14:21
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
Well, it's complicated to say something without seeing the real code and what you plan to do. You can always post a new question when you have a more complete version of it.
â Gerardo Furtado
Apr 8 at 16:05
1
1
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
This is what I meant, found a solution Plunker seems like everything works as intended, thanks for your help
â Robert Andersson
Apr 8 at 18:47
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191476%2fusing-dispatch-events-in-d3-js%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password