Skip to content

Commit 7043c13

Browse files
authored
Merge pull request #1240 from mrdawson/1213-clientside-context
Issue-1213 clientside callback_context Initial Commit
2 parents 439e13b + a9f8cc9 commit 7043c13

File tree

4 files changed

+392
-3
lines changed

4 files changed

+392
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [UNRELEASED]
6+
### Added
7+
- [#1240](https://github.com/plotly/dash/pull/1240) Adds `callback_context` to clientside callbacks (e.g. `dash_clientside.callback_context.triggered`). Supports `triggered`, `inputs`, `inputs_list`, `states`, and `states_list`, all of which closely resemble their serverside cousins.
8+
59
## [1.12.0] - 2020-05-05
610
### Added
711
- [#1228](https://github.com/plotly/dash/pull/1228) Adds control over firing callbacks on page (or layout chunk) load. Individual callbacks can have their initial calls disabled in their definition `@app.callback(..., prevent_initial_call=True)` and similar for `app.clientside_callback`. The app-wide default can also be changed with `app=Dash(prevent_initial_callbacks=True)`, then individual callbacks may disable this behavior.

dash-renderer/src/actions/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,35 @@ const getVals = input =>
525525

526526
const zipIfArray = (a, b) => (Array.isArray(a) ? zip(a, b) : [[a, b]]);
527527

528+
function inputsToDict(inputs_list) {
529+
// Ported directly from _utils.py, inputs_to_dict
530+
// takes an array of inputs (some inputs may be an array)
531+
// returns an Object (map):
532+
// keys of the form `id.property` or `{"id": 0}.property`
533+
// values contain the property value
534+
if (!inputs_list) {
535+
return {};
536+
}
537+
const inputs = {};
538+
for (let i = 0; i < inputs_list.length; i++) {
539+
if (Array.isArray(inputs_list[i])) {
540+
const inputsi = inputs_list[i];
541+
for (let ii = 0; ii < inputsi.length; ii++) {
542+
const id_str = `${stringifyId(inputsi[ii].id)}.${
543+
inputsi[ii].property
544+
}`;
545+
inputs[id_str] = inputsi[ii].value ?? null;
546+
}
547+
} else {
548+
const id_str = `${stringifyId(inputs_list[i].id)}.${
549+
inputs_list[i].property
550+
}`;
551+
inputs[id_str] = inputs_list[i].value ?? null;
552+
}
553+
}
554+
return inputs;
555+
}
556+
528557
function handleClientside(clientside_function, payload) {
529558
const dc = (window.dash_clientside = window.dash_clientside || {});
530559
if (!dc.no_update) {
@@ -544,12 +573,26 @@ function handleClientside(clientside_function, payload) {
544573
let returnValue;
545574

546575
try {
576+
// setup callback context
577+
const input_dict = inputsToDict(inputs);
578+
dc.callback_context = {};
579+
dc.callback_context.triggered = payload.changedPropIds.map(prop_id => ({
580+
prop_id: prop_id,
581+
value: input_dict[prop_id],
582+
}));
583+
dc.callback_context.inputs_list = inputs;
584+
dc.callback_context.inputs = input_dict;
585+
dc.callback_context.states_list = state;
586+
dc.callback_context.states = inputsToDict(state);
587+
547588
const {namespace, function_name} = clientside_function;
548589
let args = inputs.map(getVals);
549590
if (state) {
550591
args = concat(args, state.map(getVals));
551592
}
552593
returnValue = dc[namespace][function_name](...args);
594+
595+
delete dc.callback_context;
553596
} catch (e) {
554597
if (e === dc.PreventUpdate) {
555598
return {};

tests/integration/clientside/assets/clientside.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,30 @@ window.dash_clientside.clientside = {
5656
resolve('foo');
5757
}, 1);
5858
});
59-
}
59+
},
6060

61-
}
61+
triggered_to_str: function(n_clicks0, n_clicks1) {
62+
const triggered = dash_clientside.callback_context.triggered;
63+
return triggered.map(t => `${t.prop_id} = ${t.value}`).join(', ');
64+
},
65+
66+
inputs_to_str: function(n_clicks0, n_clicks1) {
67+
const inputs = dash_clientside.callback_context.inputs;
68+
const keys = Object.keys(inputs);
69+
return keys.map(k => `${k} = ${inputs[k]}`).join(', ');
70+
},
71+
72+
inputs_list_to_str: function(n_clicks0, n_clicks1) {
73+
return JSON.stringify(dash_clientside.callback_context.inputs_list);
74+
},
75+
76+
states_to_str: function(val0, val1, st0, st1) {
77+
const states = dash_clientside.callback_context.states;
78+
const keys = Object.keys(states);
79+
return keys.map(k => `${k} = ${states[k]}`).join(', ');
80+
},
81+
82+
states_list_to_str: function(val0, val1, st0, st1) {
83+
return JSON.stringify(dash_clientside.callback_context.states_list);
84+
}
85+
};

0 commit comments

Comments
 (0)