Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

events on charts in Vue #6

Closed
timelyportfolio opened this issue May 1, 2017 · 13 comments
Closed

events on charts in Vue #6

timelyportfolio opened this issue May 1, 2017 · 13 comments

Comments

@timelyportfolio
Copy link

Are there any examples of event handling, such as click or mouseover, with vuejs charts? I looked for chart.on but it is not defined.

@kevinwarne
Copy link
Contributor

kevinwarne commented May 1, 2017

Good question. I didn't realize this as an issue until now.

When using d2b charts without vuejs it's pretty easy to dynamically add your own event binding. Because the chart is rendered synchronously you can just programmatically setup the events after you have rendered the chart. E.g.

var sunburst = d2b.chartSunburst();
var chart = d3.select('.chart');

chart.call(sunburst);

chart.selectAll('.d2b-sunburst-arc').on('mouseover.custom', function (d) {
  // do something when a sunburst arc is moused over..
  console.log(d);
});

The vue.js components have watchers to check when the data or config have changed and then automatically call the chart updater. Probably the best way to implement this would be to have a beforeRender and rendered event hook on the vue components. So that would allow you to do something like..

<div id = 'app'>
  <sunburst-chart 
    :data = 'sunburstChartData' 
    @rendered = 'sunburstChartRendered'>
  </sunburst-chart>
</div>
var app = new Vue({
  el: '#app',
  components: {
    'sunburst-chart': d2b.vueChartSunburst
  },
  data: {
    sunburstChartData: {
      // some sunburst data
    }
  },
  methods: {
    sunburstChartRendered: function (el, chart) {
      d3.select(el).selectAll('.d2b-sunburst-arc')
        .on('mouseover.custom', function (d) {
          // do something when a sunburst arc is moused over..
          console.log(d);
        });
    }
  }
});

I'll think some more about this and work on getting something implemented tomorrow.

@kevinwarne
Copy link
Contributor

kevinwarne commented May 1, 2017

One additional note per to your original question. I've found it difficult to add events to the d2b generators themselves. E.g. chart.on(...). This is because a generator might be used for rendering many charts and then the events could have been invoked from anyone of them.

@kevinwarne
Copy link
Contributor

I published a d2b update this morning on v0.0.28. You should be able to bind events whenever the vueChartSunburst component emits a rendered event. I updated my previous answer to show how this can be used. The beforeRender and rendered events are passed the element node el and chart generator chart.

Another thing to note is that most d2b internal events are bound to the default event namespace. This means that if you are working with the d2b-sunburst-arc elements and you bind an event like d3.select('.d2b-sunburst-arc').on('click', ...) this will likely override the internal click event and you won't be able to click to zoom anymore. Name-spacing the event like d3.select('.d2b-sunburst-arc').on('click.custom', ...) should do the trick though.

Let me know if this is what you were looking for. Thanks!

@timelyportfolio
Copy link
Author

Working through it now. Will report back. Thanks!!!

@timelyportfolio
Copy link
Author

timelyportfolio commented May 1, 2017

@kevinwarne, here is what I ended up doing even though the result is still not perfect.

    sunburstChartRendered: function (el, chart) {
      var that = this;
      d3.select(el).selectAll('.d2b-sunburst-chart')
        .on('mouseover', function (d) {
          if(d3.event.target.classList[0] === 'd2b-sunburst-arc'){
            that.filtered_tree = d3.select(d3.event.target).datum().data;
          }
        });

When selecting .d2b-sunburst-arc, I would lose the click.custom handler after a couple of clicks, so I chose to move the handler to the .d2b-sunburst-chart level. Here is the live example https://bl.ocks.org/timelyportfolio/8c9a77da3e9ca57827a8801fb8a16ba9.

Thanks again!

@kevinwarne
Copy link
Contributor

kevinwarne commented May 1, 2017

@timelyportfolio This is a great example of extending the sunburst to a treemap on mouseover.

I can see why the click event is failing to work after a couple tries. It's because the internal chart zooming is actually 'exiting' nodes and then they arn't being rebound upon entering again. I'll think more about how best to achieve something like this as it is probably a common use case.

In the meantime this is a solution that would work, however it would require disabling the sunburst zooming so that nodes don't get removed..

// disable zooming on the chart
sunburstChartConfig: function () {
    chart.label(function(d){return d.name});
    chart.color(function(d){return color(d.name);})
    chart.sunburst().size(function(d){return d.x}).zoomable(false);

    // // another option if you wanted to remove the breadcrumbs  for this particular visualization
    // chart.chartFrame().breadcrumbsEnabled(false);
    // // yet another option for horizontal breadcrumbs across the top
    // chart.chartFrame().breadcrumbsOrient('top');
    // chart.breadcrumbs().vertical(false);    
}

// apply custom events
sunburstChartRendered: function (el, chart) {
  var that = this;
  d3.select(el).selectAll('.d2b-sunburst-arc')
    .on('click.custom', function (d) { // click or mouseover can be used here
      that.filtered_tree = d.data;
    });
}

Cheers!

@kevinwarne
Copy link
Contributor

kevinwarne commented May 2, 2017

@timelyportfolio I think I've come up with a solution to this d2b application.

Any d2b generators that render content to the page will fire an event when they are applied either externally or internally. This event is bubbled up through the DOM. For example:

var sunburst = d2b.chartSunburst();
var chart = d3.select('.chart').on('breadcrumbs-updated', customizeBreadcrumbs);

chart.call(sunburst);

function customizeBreadcrumbs() {
  // This function will be called whenever the breadcrumbs are updated.
};

So for your purposes we want to rebind the custom events whenever the arcs are updated (entered or exited). The d2b.svgSunburst is in charge of updating the sunburst arcs so we can expect a 'svg-sunburst-updated' event to bubble up through the DOM whenever this d2b component is updated. Something like this "should" do the trick as of v0.0.29.

<div id = 'app'>
  <sunburst-chart 
    :data = 'sunburstChartData' 
    ref = 'sunburst'
    @svg-sunburst-updated.native = 'bindArcEvents'
  >
  </sunburst-chart>
</div>
var app = new Vue({
  el: '#app',
  components: {
    'sunburst-chart': d2b.vueChartSunburst
  },
  data: {
    sunburstChartData: {
      // some sunburst data
    }
  },
  methods: {
    bindArcEvents: function () {
      var that = this,
            el = this.$refs.sunburst.$el;
      d3.select(el).selectAll('.d2b-sunburst-arc')
        .on('mouseover.custom', function (d) { // click or mouseover can be used here
          that.filtered_tree = d.data;
        });
    }
  }
});

@timelyportfolio
Copy link
Author

Very interesting, so d3 events bubble up as native? I really appreciate you looking at this!

@kevinwarne
Copy link
Contributor

kevinwarne commented May 2, 2017

So the native identifier used when binding an event for a vue component means that the event should be bound to the component's root node $el rather than the vue instance itself. So if you were listening for a click event on the entire sunburst chart component you could just use @click.native = 'someFunction'.

The vue documentation briefly touches on it here

@kevinwarne
Copy link
Contributor

@timelyportfolio I'll go ahead and close this issue for now. Feel free to reopen if you have further question or issues on the matter.

@kjhatis
Copy link

kjhatis commented Jun 6, 2018

I want get the Data(the object) on which bar/point(in case of line chart) the user have clicked. is there any way to get the exact object?

@cmburns
Copy link

cmburns commented Sep 7, 2018

I am also interested in an answer to kjhatis question.
So how can I get the event if a user clicks on a certain bar of a barchart?

Thanks...

@kevinwarne
Copy link
Contributor

Sorry, I must have missed @kjhatis message. This can be done with plain d3 after the chart has been rendered. Just bind a click event and listen for the datum in the handler.

note: the datum will actually be a wrapper of the original user supplied datum with some other d2b specific attributes available. If you want the original datum just console.log d.data in the example below instead of d

d3.selectAll('.d2b-bar-group').on('click', function (d) { console.log(d) })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants