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
APIs to interact with Selection #1830
Comments
This question in the vega-js group is very relevant to this issue. |
@arvind Can you post an example of how one might access a selection? I'm trying to build an application where you can crossfilter between different visualization. Since I want to re-query the data, I cannot use a single spec for the different charts. |
Sure. The selection states are stored in datasets named |
Why is that? Is there a way to correct the brush? |
The brush mark is currently driven by signals within each unit. It's difficult to update these signals based on updates to the backing dataset (we need to identify the matching tuple, and extract information from it). @jheer and I decided that this would be a limitation with 2.0 that we would address in subsequent releases once we better understood how users wanted to update selections via the API. |
/cc @djahandarie |
To make forward progress on this, I would like to decouple an API for writing to selections (which involves hairier design/implementation issues) from reading selections (which should hopefully be more straightforward). Here're some ideas sketching out the latter. New API Methods
Notes
I lean towards exposing selections as signals for both being the simplest, most idiomatic solution that does not require modifications to Vega internals beyond the expression functions. Moreover, these new top-level signals could also offer a cleaner entry point for a future "selection write" API (e.g., writing to these signals would update the backing dataset and any downstream signals within views). |
This is great. I think having top level signals makes sense especially if we can use them to write. I wonder whether we even need the helper functions in that case or whether the Vega view API is sufficient. |
Yeah, I went back and forth on adding Vega-Lite helper functions. I lean towards adding them (rather than relying on the Vega view API alone) to give users a forward-compatible way to access selections agnostic to the Vega we generate. Thus, we would be free to change the underlying mechanisms of how selections could work in the future. An interesting question is whether we are protected from all of this with semantic versioning. If we point users to Vega view APIs to access selections, then we're implicitly extending the semantic versioning contract to the Vega we generate. This has advantages (e.g., Lyra would certainly appreciate being able to rely on this definition of semantic versioning, as it analyzes the generated Vega). But, I'm not sure how feasible this would actually, be or how we would define what major/minor changes in Vega-Lite -> Vega generation would be... |
I don't know how forward compatible we need to be and I don't see us changing how selections are implemented anytime soon. Thus, I lean towards not providing helper functions. Every Vega-Lite version already has a minimum Vega version it depends on. We can make a promise about the specific signals while still being flexible about how we generate other parts of the spec. |
I think we should provide helper functions because it's not a realistic expectation that Vega-Lite users should know how we name the underlying data sources and signals. Plus, there is no "signal" concept in Vega-Lite, asking users to use signal APIs (which is a lower level abstraction) is a bit weird. |
Let's see how we name the signals. I'm expecting the signal names to directly correspond to the selection names. |
I need the ability to write selections as well (in order to clear them). I have a multitude of graphs that crossfilter each other and I'm not using Vega Lite's filter/transform because there's a massive amount of data and I have a zippy API that handles the filtering and aggregation. When a user makes a selection, I intercept it via And this removal is the issue. If the user interacts with a graph to clear a selection, everything works as expected because it follows the aforementioned flow (Vega Lite -> External). However, if the user removes a selection via the external component, I have no way of telling a graph to clear its selection (External -> Vega Lite). |
I have a UI that contains multiple Vega-Lite charts, each with their own selections. It would great to have a button that clears all selection states. @kanitw suggested current workarounds are (1) set the selection store data to be empty (need to know about Vega Just wanted to add to the conversation that "clear selection" would be useful to have in a selection API 🙂 |
This is hacky, but I'm setting my legend-bound selection by using SVG as my renderer, doing an XPath search of the text element in the legend, and clicking it:
|
Just adding my two cents. Vega-lite almost has full functionality in terms of interactivity. Cleaning up the selections API would fix that. For instance, one might not want to use shift + click or alt + click to generate multi selections. A click is more natural in most cases. By exposing selection getters / setters, one could listen for a click and then add or remove additional selections using this API, without using a shift/alt keypress. Further, people would be able to programmatically trigger selections without direct user interaction. This would allow a chart to live in a stateful environment, where a user can come back to a page and some default selection is fired in response to something else. Really, the possibilities are endless! People keep mentioning hacks that are available but I'm new to the Vega library. It would be nice if someone could post some examples that show how to hack the selection state until an API is available. From what little I know right now, there seem to be potential hacks for mimicking selection state by "manually" altering view aesthetics, or using the view.insert()/view.remove() methods to alter data in response to some event listener. |
Hack &
|
Here's a hack using the current version of VegaLite ( vegaEmbed(`#minimap`, vegalite_spec_for_minimap)
.then(function(minimapVe){
minimapVe.view.addSignalListener('brush', function(signalName, e) {
console.log("updated");
});
}); |
No, you should use the Vega view view api to listen for signal changes instead. https://vega.github.io/vega/docs/api/view/#signals |
Thanks @domoritz. That's really helpful. Updated my above code. |
Nice. That looks right. If you can use async/await, the code even gets a bit more readable. |
Is it possible to write to the selection from Javascript right now? I can always update all my data points and modify one of their fields, but I was hoping for a cleaner way to do this with the selection API? |
Yes, you can set the selection with the signal and data API. However, you have to reverse engineer the right format. |
After much research I made this example of how to change the vega-lite brush programmatically https://observablehq.com/@john-guerra/update-vega-lite-brush-programmatically Using @koaning example this stack overflow question I figured that you can change the brush by updating "brush_y" (assuming that your selection is called brush) or change the selection using "brush_tuple" (which doesn't seem to update the brush mark) |
I am running into this as well. @john-guerra I forked your notebook here https://observablehq.com/@dmarr/update-vega-lite-brush-programmatically with a fix. Seems the way you were loading the vega-dataset no longer works on observable. My issue is that I have an external control (vue) that I need to bind with the brush value. So I can listen to brush signals thusly: view.addSignalListener(
'brush_yearmonthdate_timestamp',
debounce((_, range) => {
emit('change', range);
}, 10)
); and outside of the vega component I can respond to the change to update my vue control. However, if I want to set the brush due to user changing the vue input, I am having trouble: // start and end are external values from a date range selector
const range = [start, end].map(time => view.scale('x')(time));
const currentRange = view.signal('brush_x');
if (range[0] !== currentRange[0] || range[1] !== currentRange[1]) {
view.signal('brush_x', range).runAsync();
} That runs when my vega component receives new prop values (from external change). So basically the component gets a new prop on signal change and re-runs its internal listener. Seems like I should be able to change the brush value without firing the signal listener, but maybe I'm missing another option. Any help would be appreciated. Thanks! |
So after reading all of this i still cannot manage to get the array of selected objects using the Vega-Lite API. I very simply want to aggregate a set of selected objects from a multi-click event and test for inclusion in this set as a coloring condition. Because i am working at different levels of detail, I cannot get the built-in toggle functionality to work properly. And if I cannot access the array directly I have no way of fixing that behaviour. My apologies if I am making stupid errors but this is very frustrating. |
No description provided.
The text was updated successfully, but these errors were encountered: