Question

I have written small web page that creates a graph with alarms distribution over 24 hour period, with alarm count being a variable that drives how many alarms will be send in this period.

After that I have written a small form that will have the alarm count variable in a input field

<input id="alarm_count_input" type="number"/>

Now I would like to be able to redraw the d3.js graph without page reload, taking alarm count that user submits with the form / input field.

JsFiddle : http://jsfiddle.net/L42LU/

Code :

<script type="text/javascript">
$(function(){

........

function createDataForD3( alarm_cnt, array) {
    var data = [];
    for(var i = 0, n=array.length; i< n; i++)
    {
        data.push({ "hour": i, 
                    "alarms": getRatioForHour(i, array) * alarm_cnt
                    });
    }
    return data;
}

var schedule = [1, 1, 1, 1, 4, 1, 1, 3, 2, 3, 4, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1];
var alarm_count = 1000;

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 0.85 * document.width - margin.left - margin.right,
    height = 0.9 * document.height - margin.top - margin.bottom;

var x_extent = [-0.5, 23.5];
var y_extent = [0, Math.round(
                    getMaxAlarmCountForHourInArray(alarm_count, schedule)[1] * 1.2 ) ];

var x_scale = d3.scale.linear()
    .range([0, width])
    .domain(x_extent);

var y_scale = d3.scale.linear()
    .range([ height, 0])
    .domain(y_extent);


var data = createDataForD3( alarm_count , schedule);

var xAxis = d3.svg.axis()
    .scale(x_scale)
    .orient("bottom")
    .ticks(24);

var yAxis = d3.svg.axis()
    .scale(y_scale)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) { return x_scale(d.hour) ; })
    .y(function(d) { return y_scale( Math.round( d.alarms)) ; });

var svg = d3.select("body")
    .append("svg")
        .attr("class", "chart")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
    .append("text")
        .attr("y", margin.bottom)
        .attr("x", (width) / 2)
        .text("Hour");

    svg.append("g")
        .classed("x grid ", true)
        .attr("transform", "translate(0,"+height+")")
        .call(xAxis
          .tickSize(-height,0,0)
          .tickFormat("")
    );

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 8)
      .attr("x", -20)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Alarms per hour");

    svg.append("g")
        .classed("y grid", true)
        .call(yAxis
            .tickSize(-width,0,0)
            .tickFormat("")
    )

    svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);

    svg.append("g")
      .selectAll("circle")
        .data(data)
      .enter()
      .append("circle");

    d3.selectAll("circle")
        .attr("cx", function(d){ return x_scale(d.hour); })
        .attr("cy", function(d){ return y_scale(d.alarms); })
        .attr("r", 6);

    $("#alarm_count_input").val(alarm_count);
});
</script>
<form id="form1" role="form" onSubmit="return false;">
    <div class=form-group">
        <label for="alarm_count_input" >Alarm count</label><br>
        <input id="alarm_count_input" class="form-control" type="number" placeholder="Alarm count" min="0" max="999999" step="1" />
    </div>
</form>
</body>
Was it helpful?

Solution

Here, is a jsfiddle with an updated code. I moved all parts of code that are depended on alarm count into a seperate function which gets called each time alarm count value is changed.

function render(){    
    yAxis = yAxis.tickFormat(d3.format(","));
    y_extent = [0, Math.round(
                    getMaxAlarmCountForHourInArray(alarm_count, schedule)[1] * 1.2 ) ];

    y_scale.domain(y_extent);

    var data = createDataForD3( alarm_count , schedule);


    svg.select("g.y.axis").call(yAxis);

    gridAxis.tickFormat("");
    svg.select("g.y.grid").call(gridAxis);

    var lineData = svg.selectAll(".line").data([data]);

    lineData.enter().append("path")
      .attr("d", line(data))
    .attr("class", "line");

    lineData.exit().remove();

    var svgData = svg.selectAll("circle")
        .data(data);

     var svgEnter = svgData.enter().append("circle")
        .attr("cx", function(d){ return x_scale(d.hour); })
        .attr("cy", function(d){ return y_scale(d.alarms); })
        .attr("r", 6);

    svgData.exit().remove();

    }
render();

OTHER TIPS

I am posting my solution as well (someone might find it useful). I have done similar approach to Yogesh's but I explicitly remove and add the elements again (don't know if that's the correct approach - maybe someone will point some errors to me).

function drawY(alarm_cnt, array) {

    d3.selectAll(".circles").remove();
    d3.selectAll(".y").remove();
    d3.selectAll(".line").remove();

    var data = createDataForD3( alarm_cnt , array);

    var y_extent = [0, Math.round(
                    getMaxAlarmCountForHourInArray(alarm_cnt, array)[1] * 1.2 ) ];

    var y_scale = d3.scale.linear()
        .range([ height, 0])
        .domain(y_extent);

    var yAxis = d3.svg.axis()
        .scale(y_scale)
        .orient("left");

    var line = d3.svg.line()
        .x(function(d) { return x_scale(d.hour) ; })
        .y(function(d) { return y_scale( Math.round( d.alarms)) ; });

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 8)
      .attr("x", -20)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Alarms per hour");

    svg.append("g")
        .classed("y grid", true)
        .call(yAxis
            .tickSize(-width,0,0)
            .tickFormat("")
    )

    svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);

    svg.append("g")
        .attr("class", "circles")
        .selectAll("circle")
        .data(data)
      .enter()
      .append("circle");

    d3.selectAll("circle")
        .attr("cx", function(d){ return x_scale(d.hour); })
        .attr("cy", function(d){ return y_scale( Math.round( d.alarms)); })
        .attr("r", 6);
}

var margin = {top: 20, right: 20, bottom: 30, left: 70},
    width = 0.85 * document.width - margin.left - margin.right,
    height = 0.9 * document.height - margin.top - margin.bottom;

var schedule = [1, 2, 1, 2, 2, 2, 1, 4, 4, 3, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
var alarm_count = 1000;

var data = createDataForD3( alarm_count , schedule);

var x_extent = [-0.8, 23.5];

var x_scale = d3.scale.linear()
    .range([0, width])
    .domain(x_extent);

var xAxis = d3.svg.axis()
    .scale(x_scale)
    .orient("bottom")
    .ticks(24);


var svg = d3.select("body")
    .append("svg")
        .attr("class", "chart")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
    .append("text")
        .attr("y", margin.bottom)
        .attr("x", (width) / 2)
        .text("Hour");

    svg.append("g")
        .classed("x grid ", true)
        .attr("transform", "translate(0,"+height+")")
        .call(xAxis
          .tickSize(-height,0,0)
          .tickFormat("")
    );


    drawY(alarm_count, schedule);
    $("#alarm_count_input").val(alarm_count);

    $("#alarm_count_input").change(function(){ 
       drawY($("#alarm_count_input").val(), schedule);
    });
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top