Skip to content

Commit b03281b

Browse files
authored
Merge pull request #81 from plotly/prevent_initial_call
Prevent initial call
2 parents 4b2f015 + 1d67978 commit b03281b

File tree

9 files changed

+249
-84
lines changed

9 files changed

+249
-84
lines changed

src/app/callbacks.jl

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
dependency_id_string(id::NamedTuple) = sorted_json(id)
22
dependency_id_string(id::String) = sorted_json(id)
33

4-
function dependency_string(dep::Dependency{Trait, String}) where {Trait}
4+
function dependency_string(dep::Dependency{Trait, String}) where {Trait}
55
return "$(dep.id).$(dep.property)"
66
end
77

8-
function dependency_string(dep::Dependency{Trait, <:NamedTuple}) where {Trait}
8+
function dependency_string(dep::Dependency{Trait, <:NamedTuple}) where {Trait}
99
id_str = replace(
1010
sorted_json(dep.id),
1111
"."=>"\\."
@@ -33,7 +33,7 @@ end
3333
state::Union{Vector{State}, State} = []
3434
)
3535
36-
Create a callback that updates the output by calling function `func`.
36+
Create a callback that updates the output by calling function `func`.
3737
3838
# Examples
3939
@@ -59,9 +59,10 @@ function callback!(func::Union{Function, ClientsideFunction, String},
5959
app::DashApp,
6060
output::Union{Vector{<:Output}, Output},
6161
input::Union{Vector{<:Input}, Input},
62-
state::Union{Vector{<:State}, State} = State[]
62+
state::Union{Vector{<:State}, State} = State[];
63+
prevent_initial_call = nothing
6364
)
64-
return _callback!(func, app, CallbackDeps(output, input, state))
65+
return _callback!(func, app, CallbackDeps(output, input, state), prevent_initial_call = prevent_initial_call)
6566
end
6667

6768
"""
@@ -70,7 +71,7 @@ end
7071
deps...
7172
)
7273
73-
Create a callback that updates the output by calling function `func`.
74+
Create a callback that updates the output by calling function `func`.
7475
"Flat" version of `callback!` function, `deps` must be ``Output..., Input...[,State...]``
7576
7677
# Examples
@@ -85,7 +86,7 @@ app = dash() do
8586
8687
end
8788
end
88-
callback!(app,
89+
callback!(app,
8990
Output("outputID2", "children"),
9091
Output("outputID", "children"),
9192
Input("graphTitle", "value"),
@@ -97,13 +98,14 @@ end
9798
"""
9899
function callback!(func::Union{Function, ClientsideFunction, String},
99100
app::DashApp,
100-
deps::Dependency...
101+
deps::Dependency...;
102+
prevent_initial_call = nothing
101103
)
102104
output = Output[]
103105
input = Input[]
104106
state = State[]
105107
_process_callback_args(deps, (output, input, state))
106-
return _callback!(func, app, CallbackDeps(output, input, state, length(output) > 1))
108+
return _callback!(func, app, CallbackDeps(output, input, state, length(output) > 1), prevent_initial_call = prevent_initial_call)
107109
end
108110

109111
function _process_callback_args(args::Tuple{T, Vararg}, dest::Tuple{Vector{T}, Vararg}) where {T}
@@ -115,20 +117,29 @@ end
115117
function _process_callback_args(args::Tuple, dest::Tuple{Vector{T}, Vararg}) where {T}
116118
_process_callback_args(args, Base.tail(dest))
117119
end
118-
function _process_callback_args(args::Tuple, dest::Tuple{})
120+
function _process_callback_args(args::Tuple, dest::Tuple{})
119121
error("The callback method must received first all Outputs, then all Inputs, then all States")
120122
end
121-
function _process_callback_args(args::Tuple{}, dest::Tuple{})
123+
function _process_callback_args(args::Tuple{}, dest::Tuple{})
122124
end
123125

124126

125-
function _callback!(func::Union{Function, ClientsideFunction, String}, app::DashApp, deps::CallbackDeps)
126-
127+
function _callback!(func::Union{Function, ClientsideFunction, String}, app::DashApp, deps::CallbackDeps; prevent_initial_call)
128+
127129
check_callback(func, app, deps)
128-
130+
129131
out_symbol = Symbol(output_string(deps))
130-
callback_func = make_callback_func!(app, func, deps)
131-
push!(app.callbacks, out_symbol => Callback(callback_func, deps))
132+
callback_func = make_callback_func!(app, func, deps)
133+
push!(
134+
app.callbacks,
135+
out_symbol => Callback(
136+
callback_func,
137+
deps,
138+
isnothing(prevent_initial_call) ?
139+
get_setting(app, :prevent_initial_callbacks) :
140+
prevent_initial_call
141+
)
142+
)
132143
end
133144

134145

src/app/config.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ struct DashConfig
66
routes_pathname_prefix ::String
77
assets_folder ::String
88
assets_url_path ::String
9-
assets_ignore ::String
9+
assets_ignore ::String
1010
serve_locally ::Bool
1111
suppress_callback_exceptions ::Bool
12+
prevent_initial_callbacks ::Bool
1213
eager_loading ::Bool
13-
meta_tags ::Vector{Dict{String, String}}
14+
meta_tags ::Vector{Dict{String, String}}
1415
assets_external_path ::Union{String, Nothing}
1516
include_assets_files ::Bool
1617
show_undo_redo ::Bool

src/app/dashapp.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,15 @@ If a parameter can be set by an environment variable, that is listed as:
265265
if your layout is dynamic, to bypass these checks.
266266
env: `DASH_SUPPRESS_CALLBACK_EXCEPTIONS`
267267
268+
- `prevent_initial_callbacks::Bool`: Default ``false``: Sets the default value
269+
of ``prevent_initial_call`` for all callbacks added to the app.
270+
Normally all callbacks are fired when the associated outputs are first
271+
added to the page. You can disable this for individual callbacks by
272+
setting ``prevent_initial_call`` in their definitions, or set it
273+
``true`` here in which case you must explicitly set it ``false`` for
274+
those callbacks you wish to have an initial call. This setting has no
275+
effect on triggering callbacks when their inputs change later on.
276+
268277
- `show_undo_redo::Bool`: Default ``false``, set to ``true`` to enable undo
269278
and redo buttons for stepping through the history of the app state.
270279
@@ -283,6 +292,7 @@ function dash(;
283292
assets_ignore = "",
284293
serve_locally = true,
285294
suppress_callback_exceptions = dash_env(Bool, "suppress_callback_exceptions", false),
295+
prevent_initial_callbacks = false,
286296
eager_loading = false,
287297
meta_tags = Dict{Symbol, String}[],
288298
index_string = default_index,
@@ -307,6 +317,7 @@ function dash(;
307317
assets_ignore,
308318
serve_locally,
309319
suppress_callback_exceptions,
320+
prevent_initial_callbacks,
310321
eager_loading,
311322
meta_tags,
312323
assets_external_path,

src/app/supporttypes.jl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ struct Dependency{Trait, IdT <: Union{String, NamedTuple}}
2222
end
2323

2424
dep_id_string(dep::Dependency{Trait, String}) where {Trait} = dep.id
25-
dep_id_string(dep::Dependency{Trait, <:NamedTuple}) where {Trait} = sorted_json(dep.id)
25+
dep_id_string(dep::Dependency{Trait, <:NamedTuple}) where {Trait} = sorted_json(dep.id)
2626

27-
dependency_tuple(dep::Dependency) = (id = dep_id_string(dep), property = dep.property)
27+
dependency_tuple(dep::Dependency) = (id = dep_id_string(dep), property = dep.property)
2828

2929
const Input = Dependency{TraitInput}
3030
const State = Dependency{TraitState}
@@ -37,7 +37,7 @@ We use "==" to denote two deps that refer to the same prop on
3737
the same component. In the case of wildcard deps, this means
3838
the same prop on *at least one* of the same components.
3939
"""
40-
function Base.:(==)(a::Dependency, b::Dependency)
40+
function Base.:(==)(a::Dependency, b::Dependency)
4141
(a.property == b.property) && is_id_matches(a, b)
4242
end
4343

@@ -59,7 +59,7 @@ end
5959
is_id_matches(a::Dependency{Trait1, String}, b::Dependency{Trait2, String}) where {Trait1, Trait2} = a.id == b.id
6060
is_id_matches(a::Dependency{Trait1, String}, b::Dependency{Trait2, <:NamedTuple}) where {Trait1, Trait2} = false
6161
is_id_matches(a::Dependency{Trait1, <:NamedTuple}, b::Dependency{Trait2, String}) where {Trait1, Trait2} = false
62-
function is_id_matches(a::Dependency{Trait1, <:NamedTuple}, b::Dependency{Trait2, <:NamedTuple}) where {Trait1, Trait2}
62+
function is_id_matches(a::Dependency{Trait1, <:NamedTuple}, b::Dependency{Trait2, <:NamedTuple}) where {Trait1, Trait2}
6363
(Set(keys(a.id)) != Set(keys(b.id))) && return false
6464

6565
for key in keys(a.id)
@@ -70,12 +70,12 @@ function is_id_matches(a::Dependency{Trait1, <:NamedTuple}, b::Dependency{Trait2
7070

7171
a_wild = is_wild(a_value)
7272
b_wild = is_wild(b_value)
73-
73+
7474
(!a_wild && !b_wild) && return false #Both not wild
75-
!(a_wild && b_wild) && continue #One wild, one not
75+
!(a_wild && b_wild) && continue #One wild, one not
7676
((a_value == ALL) || (b_value == ALL)) && continue #at least one is ALL
7777
((a_value == MATCH) || (b_value == MATCH)) && return false #one is MATCH and one is ALLSMALLER
78-
78+
7979
end
8080
return true
8181
end
@@ -100,6 +100,7 @@ end
100100
struct Callback
101101
func ::Union{Function, ClientsideFunction}
102102
dependencies ::CallbackDeps
103+
prevent_initial_call ::Bool
103104
end
104105

105106
is_multi_out(cb::Callback) = cb.dependencies.multi_out == true
@@ -108,7 +109,7 @@ get_output(cb::Callback, i) = cb.dependencies.output[i]
108109
first_output(cb::Callback) = first(cb.dependencies.output)
109110

110111
struct PreventUpdate <: Exception
111-
112+
112113
end
113114

114115

src/handler/state.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ _dep_clientside_func(func::ClientsideFunction) = func
2323
_dep_clientside_func(func) = nothing
2424
function _dependencies_json(app::DashApp)
2525
result = map(values(app.callbacks)) do callback
26-
(inputs = dependency_tuple.(callback.dependencies.input),
27-
state = dependency_tuple.(callback.dependencies.state),
28-
output = output_string(callback.dependencies),
29-
clientside_function = _dep_clientside_func(callback.func)
26+
(
27+
inputs = dependency_tuple.(callback.dependencies.input),
28+
state = dependency_tuple.(callback.dependencies.state),
29+
output = output_string(callback.dependencies),
30+
clientside_function = _dep_clientside_func(callback.func),
31+
prevent_initial_call = callback.prevent_initial_call
3032
)
3133
end
3234
return JSON2.write(result)
@@ -52,7 +54,7 @@ make_reload_state(app::DashApp) = get_devsetting(app, :hot_reload) ? StateReload
5254
get_cache(state::HandlerState) = state.cache
5355

5456
function rebuild_cache!(state::HandlerState)
55-
cache = get_cache(state)
57+
cache = get_cache(state)
5658
(cache.resources, cache.index_string, cache.dependencies_json) = _cache_tuple(state.app, state.registry)
5759
cache.need_recache = false
5860
end

0 commit comments

Comments
 (0)