Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 
wandapec
Partner - Contributor III
Partner - Contributor III

Extensions Help - D3Js.org

Hi guys,

I recently decided to try get a better understanding of extensions and try to use some of the D3js.org stuff in my models. I have managed to get the basics of extensions sorted.

However, after going through all the training manuals and examples on the web, I think I am really close, but seem to be missing something and I really don’t know what. I have attached a link to the example I am working on - trying to get a basic Bar Chart working… Would someone be able to take a look and see where I am going wrong, please?

Thanks in advance.

Dropbox - barexample.zip

1 Solution

Accepted Solutions
Brian_Munz
Employee
Employee

To add clicking on the individual bars, you need to attach a click even to the cars.  For the code you've posted here, you would change your bar code to this:

svg.selectAll(".bar").data(dataJson).enter().append("rect").attr("class", "bar").attr("x", function(d) {

  return x(d.id);

  }).attr("width", x.rangeBand()).attr("y", function(d) {

  return y(d.data);

  }).attr("height", function(d) {

  return height - y(d.data);

  }).on("click",function(d){

  _this.Data.SelectTextsInColumn(0, false, d.id);

  });

The new code is bolded.  It takes the bar ID and selects it in QlikView.  In order to allow the selection of multiple bars you'd need to code some sort of ability to select multiple bars like a select box, lasso, etc.

View solution in original post

19 Replies
wandapec
Partner - Contributor III
Partner - Contributor III
Author

Pretty please....

wandapec
Partner - Contributor III
Partner - Contributor III
Author

//AJL: will need to make sure this is updated whenever the final extension is packaged.  I.E. the folder name will need to be whatever the final product name will be

var extensionName = "barexample";

var extensionPath = Qva.Remote + "?public=only&name=Extensions/" + extensionName +"/";

function barexample_Init() {

  var files = [];

  //if Can't resolve jQuery, then load it

  if (typeof jQuery == 'undefined') {

  files.push(extensionPath + "jquery.js");

  }

  //Pushing js file names into files array

  files.push(extensionPath + "json2.js");

  files.push(extensionPath + "d3.v3.min.js");

  //loading all the js files and then executing extension_Done

  Qv.LoadExtensionScripts(files, barexample_Done);}

function barexample_Done() {

    Qva.AddExtension(extensionName, function () {

  Qva.LoadCSS(extensionPath + "style.css");

    var _this = this;

//   //get first text box

// //var text1 = _this.Layout.Text0.text;

// var text1 = _this.Layout.Text0.text.toString();

// //get check box value

// var checkbox1 = _this.Layout.Text1.text.toString();

// var select = _this.Layout.Text2.text.toString();

  //add a unique name to the extension in order to prevent conflicts with other extensions.

  //basically, take the object ID and add it to a DIV

  var divName = _this.Layout.ObjectId.replace("\\", "_");

  if(_this.Element.children.length == 0) {//if this div doesn't already exist, create a unique div with the divName

  var ui = document.createElement("div");

  ui.setAttribute("id", divName);

  _this.Element.appendChild(ui);

  } else {

  //if it does exist, empty the div so we can fill it again

  $("#" + divName).empty();

  }

  //ATN: The below 2 lines will show you a demo- writing HTML into the extension box.

  //var html = "test html";

  //$("#" + divName).html(html);

  //Data Elements

  var dataset = [];

  var textset = [];

  var data = _this.Data;

  for (var f=0; f < data.Rows.length; f++ ) {

  var row = data.Rows;

  //Dimension 1 : Defined in the Definition.xml

  var dim1 = row[0].text;

  //Dimension 2 : Defined in the Definition.xml

  var measure1 = row[1].text;

  letter = textset.concat(dim1);

  frequency = dataset.concat(measure1);

  }

var margin = {top: 20, right: 20, bottom: 30, left: 40},

    width = 960 - margin.left - margin.right,

    height = 500 - margin.top - margin.bottom;

var formatPercent = d3.format(".0%");

var x = d3.scale.ordinal()

  .domain(data.map(function(d) { return d.letter; }))

    .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()

    .domain([0, d3.max(data, function(d) { return d.frequency; })])

    .range([height, 0]);

var xAxis = d3.svg.axis()

    .scale(x)

    .orient("bottom");

var yAxis = d3.svg.axis()

    .scale(y)

    .orient("left")

    .tickFormat(formatPercent);

var svg = d3.select("body").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 + ")");

 

  svg.append("g")

      .attr("class", "x axis")

      .attr("transform", "translate(0," + height + ")")

      .call(xAxis);

  svg.append("g")

      .attr("class", "y axis")

      .call(yAxis)

    .append("text")

      .attr("transform", "rotate(-90)")

      .attr("y", 6)

      .attr("dy", ".71em")

      .style("text-anchor", "end")

      .text("Frequency");

  svg.selectAll(".bar")

      .data(data)

    .enter().append("rect")

      .attr("class", "bar")

      .attr("x", function(d) { return x(d.letter); })

      .attr("width", x.rangeBand())

      .attr("y", function(d) { return y(d.frequency); })

      .attr("height", function(d) { return height - y(d.frequency); });

function type(d) {

  d.frequency = +d.frequency;

  return d;

}

});

}

// Call the initialization function

barexample_Init();

danielrozental
Master II
Master II

So Steve, did you get it to work?

Clever_Anjos
Employee
Employee

This line is returning an error, check it

var x = d3.scale.ordinal()

  .domain(data.map(function(d) { return d.letter; }))

    .rangeRoundBands([0, width], .1);

Clever_Anjos
Employee
Employee

I don´t know if this.Data exposes a "map" method,

wandapec
Partner - Contributor III
Partner - Contributor III
Author

Thanks Clever. I am not sure what to check or change... The original script to fetch the data was like this...

  d3.tsv("data.tsv", type, function(error, data) {
  x.domain(data.map(function(d) { return d.letter; }));
  y.domain([0, d3.max(data, function(d) { return d.frequency; })]);

That was to fetch data out of a .tsv file...

The d3 API says this... 

# ordinal.range([values])

If values is specified, sets the output range of the ordinal scale to the specified array of values. The first element in the domain will be mapped to the first element in values, the second domain value to the second range value, and so on. If there are fewer elements in the range than in the domain, the scale will recycle values from the start of the range. If values is not specified, this method returns the current output range.

This method is intended for when the set of discrete output values is computed explicitly, such as a set of categorical colors. In other cases, such as determining the layout of an ordinal scatterplot or bar chart, you may find the rangePoints or rangeBands operators more convenient.

wandapec
Partner - Contributor III
Partner - Contributor III
Author

Not yet... Will keep you posted..

danielrozental
Master II
Master II

Steve, Stefan Walther did a D3.js extension you should check it out.

Creating a QlikView Extension from Scratch using D3 (QlikTip #50)

Not applicable

Hey Steve, I actually wrote the original d3Bar example and just pushed a revised version to GitHub (wallyflops/d3Graph · GitHub).

By looking at the code you posted, I can see that you are looping through and capturing information from _this.Data.Rows but you are never returning the data values to a parent object.  Later on, when you are referencing "data" it is still set to the parent object of  _this.Data, which the objects of letter or frequency would not exist.

To make it clearer, check this portion of my script: d3Graph/d3Bar/Script.js at master · wallyflops/d3Graph · GitHub