Black Lives Matter. Please consider donating to Black Girls Code today.

Using JavaScript to Fire Callbacks

I wasn’t sure if I should start a new topic on this or not. But I’m stuck again this time on returning a javascript variable back to dash.

my javascript looks like this

$('div[id^=row]').click(function(){
        var rowId = $(this).closest('div').attr('id');
        Shiny.onInputChange("catSelected", rowId); #(Shiny.) had been removed from the actual js file.
    });

Now the Shiny.onInputChange is obviously an R/Shiny component which I catch with a reactive function in R. Is there an equivalent in Dash. looking at the callback functions I’ve tried this

@app.callback(
    Output(component_id='my-div', component_property='children'),
    [Input('catSelected')]
    )

But that obviously doesn’t work as there is no value. Right now I’m just trying to output what I have clicked on as this will be used later to filter out a dataframe, well hopefully.

The end results is if I click on the grid and it returns the id of row_3_2, I need to split that to be able to filter a data frame on the result of the click.

Hope that makes sense.

Thanks
Residnt

The only way to do this right now is through the official Dash plugin system: https://plot.ly/dash/plugins. You can write a custom component in JavaScript with React.js, and use the function updateProps to update the component’s properties and send those properties back to the Dash server.

For this particular example, can you just target the div particular with the n_clicks property?

I’m investigating using the n_clicks property based on the example you have posted here

is-there-a-way-to-only-update-on-a-button-press-for-apps-where-updates-are-slow

However what isn’t clear at the moment is how do I know what div to target as the input since the id’s are generated during the initialization with this code

#Class that defines the row div for the cards to be added to
def gridRow(r, col, df):
    card = []
    for c in range(1, int(col(0).item()) + 1):
        card.append(makeCard(r, c, df))
    return html.Div(card, className = 'row', id = str(r))

#Class which defines the data that makes up the cards
def makeCard(r, c, df):
    tempDf = pd.DataFrame(df[(df.col_index == c) & (df.grp == r)][['GameCategory','UCount']])
    return html.Div([
        html.Div([
            html.Div([
                html.Div(tempDf['GameCategory'], className = 'boxHeader'),
                html.Div('LTD Unique Users:', className = 'boxLabel'),
                html.Div(tempDf['UCount'], className = 'boxNumbers')
            ], className = 'boxText')
        ], className = 'innerBox' )
    ], className = 'cardBox', id= '_'.join(['row', str(r), str(c)]))

Basically I end up with a 6 x 3 grid, which populates with data that I’m pushing to it. So the divs will have an id range between row_1_1 and row_3_6. However these numbers can change based on the data supplied to it. So in order to know which one to target I would need to know which one I clicked on, unless what you are saying is I need to create a callback for each iteration which wouldn’t make a lot of sense to do unless i’m missing something.

As for creating the plugin, I’ll need to look into that further based on the example on how to do this.

Yeah, (for now) you need to create a unique callback for each unique ID. See Callback for Dynamically created Graph

So here is where I’m heading so far which I don’t know if this is going to work. To test this out I’ve created a div called clicked and clickedvalue

html.Div('Nothing has been clicked', className = 'row', id = 'Clicked'),
        html.Div(className = 'row', id = 'ClickedValue')

I’m using this javascript to populate the value of the clicked div

$('div[id^=row]').click(function(){
       //alert($(this).closest('div').attr('id'));
	    var rowId = $(this).closest('div').attr('id');
        $("#Clicked").html(rowId);
    });

and it does update the actual div on screen. What I’m still trying to figure out is if I can read in this updated value with this call back

@app.callback(
        Output(component_id = 'ClickedValue', component_property = 'children'),
        [Input(component_id = 'Clicked', component_property = 'children')]
        )

def update_output_div(input_value):
    return input_value

I do get the initial value of ‘Nothing has been clicked’ but it doesn’t seem to be updating after that point.

It looks like you have multiple click events on the same element - Dash is listening for a click event (see https://github.com/plotly/dash-html-components/blob/6d04a70f098bb8640545467255ae382e8e17cffb/src/components/Div.react.js#L7-L10) and then you are updating the html element on click. The order of these events isn’t defined, so there is no guarentees that Dash will fire the click event after the div has updated on click.

Maybe this can explain it a little better
http://recordit.co/cCporQFQaW

so the div the gets updated with row_#_# is the clicked div
the div that stays Nothing has been selected is the ClickedValue div.

So from what I understand is ClickedValue isn’t being updated because Clicked isn’t actually sending a clicked event.

Yeah, it could be that your click handlers are masking the existing click handlers in the React component.

so here is something interesting. Doing this callback

@app.callback(
        Output('ClickedValue', 'children'),
        [Input('Clicked', 'n_clicks')],
         state=[State('Clicked', 'children')]
        )
def update_output_div(n_clicks, Clicked):
    return '{} but clicked {} times'.format(Clicked, n_clicks)

I get this type of output

row_3_1
Nothing clicked but clicked 13 times

So it is working however it looks like it doesn’t get the updated children attribute from the the Clicked div. So my guess is that it doesn’t send that element back to the server.

Looking at the Div code you sent earlier for the click event

onClick={() => {
                    if (props.setProps) props.setProps({n_clicks: props.n_clicks + 1});
                    if (props.fireEvent) props.fireEvent({event: 'click'});
                }}

I would need to add something like

if (props.setProps) props.setProps({children: props.children});

In order for that to be sent back. But if I was going to go that far then it might make more sense to create a plugin like you suggested to just return the id to begin with.

Ok, I’m bringing up my old thread as I didn’t really want to start a new one for this. I’ve been looking at generating my own component, actually I just wanted to modify the existing div in this instance to see if I can create a new prop call n_id to return the div id on the fire event.

However I’m running into tons of errors with NPM creating -P folders and such. Since I’m on windows 7 I think some of the scripts are causing issues while trying to run rm -rf lib/* && mkdir -p lib type commands as I get a -p and a lib folder created. where are I believe the -p is a *nix command for their mkdir.

I am currently trying to use the Ananconda command prompt or even the cmd.exe and what I’m wondering what should I be using instead of these?

Thanks

Hey residnt,

Did you solve this? I am trying to do this as well i.e. bridge two apps. I would appreciate any help. Did you manage to create a component?

There have been some improvements recently to help people building their own components, did you check https://dash.plot.ly/plugins ?

Hi, how much work would be to add this ‘id’ or a classname property? I suppose this is a lot of work since this is a basic feature and all people creating html programatically is interested on that simple thing but there is not a solution yet.

My case:
I tried to detect components by js and pass them to Python. Why?

I am generating a lot of buttons, I need the classname.
Creating near to 6000 callbacks is not feasible, app turns buggy. Creating a callback with 6000 inputs leads to a problem with the callaback function inputs declaration, if it can be done, I am sure it would be buggy also. I thought the same as residnt. Use js and pass the variable to python. It’s impossible, no matter how hard I tried to find the vulnerability, dash refresh the DOM after a click callback is fired, we can’t simulate an user input from browser(this would make a hidden dcc.Input perfect in order to store js data and fire its callaback in Python)…

Pros: I can assign all my buttons the same id and a different className. Then, with ONLY ONE callback with one input I would be able to catch the button clicked. It is efficient and quick.

So, I want to do that and I want to share this plugin with the community. But I don’t know how to do that since I dont know anything about react. Some guidance would be appreciated, the list of precise steps I should follow.

Thanks in advance!

The steps are documented in https://dash.plot.ly/plugins :slight_smile:

Also, we’re working on a “React for Python Devs” guide here: https://github.com/plotly/dash-docs/pull/116

1 Like

Hi, you can try visdcc.Run_js, it allows you run javascript in callback .:smile:

1 Like

Hi Jimmy,

How do I run a Javascript code in a callback? Example JS code given below which I want to run

anychart.onDocumentReady(function () {

var data = [
{“x”: “Mandarin chinese”, “value”: 1090000000, category: “Sino-Tibetan”},
{“x”: “English”, “value”: 983000000, category: “Indo-European”},
{“x”: “Hindustani”, “value”: 544000000, category: “Indo-European”},
{“x”: “Spanish”, “value”: 527000000, category: “Indo-European”},
{“x”: “Arabic”, “value”: 422000000, category: “Afro-Asiatic”},
{“x”: “Malay”, “value”: 281000000, category: “Austronesian”},
{“x”: “Russian”, “value”: 267000000, category: “Indo-European”},
{“x”: “Bengali”, “value”: 261000000, category: “Indo-European”},
{“x”: “Portuguese”, “value”: 229000000, category: “Indo-European”},
{“x”: “French”, “value”: 229000000, category: “Indo-European”},
{“x”: “Hausa”, “value”: 150000000, category: “Afro-Asiatic”},
{“x”: “Punjabi”, “value”: 148000000, category: “Indo-European”},
{“x”: “Japanese”, “value”: 129000000, category: “Japonic”},
{“x”: “German”, “value”: 129000000, category: “Indo-European”},
{“x”: “Persian”, “value”: 121000000, category: “Indo-European”}
];

// create a tag cloud chart
var chart = anychart.tagCloud(data);

// set the chart title
chart.title(‘15 most spoken languages’)
// set array of angles, by which words will be placed
chart.angles([0, -45, 90])
// enable color range
chart.colorRange(true);
// set color range length
chart.colorRange().length(‘80%’);

// format tooltips
var formatter = “{%value}{scale:(1)(1000)(1000)(1000)|()( thousand)( million)( billion)}”;
var tooltip = chart.tooltip();
tooltip.format(formatter);

// add an event listener
chart.listen(“pointClick”, function(e){
var url = “https://en.wikipedia.org/wiki/” + e.point.get(“x”);
window.open(url, “_blank”);
});

// display chart
chart.container(“container”);
chart.draw();
});

Thanks

I replied here about how to use a Callback to trigger some Javascript code

In the provided examples, we trigger a JS function that forces the windows to resize after a graph was updated.

I think it might help you.

Do you aim to trigger a callback from a JS script, or to trigger a JS script from a Callback?

just put your javascript code to the ‘run’ prop. of run_js component. :slightly_smiling_face:
see the example code https://github.com/jimmybow/visdcc#open-url-on-new-window-