Skip to content

Commit 135bff5

Browse files
committed
layout + checks on callback!
1 parent 66bb539 commit 135bff5

File tree

5 files changed

+77
-32
lines changed

5 files changed

+77
-32
lines changed

README.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import Pkg; Pkg.add("Dash")
1212
### Basic application
1313

1414
```jldoctest
15-
julia> import HTTP
1615
julia> using Dash
17-
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]) do
18-
html_div() do
16+
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
17+
18+
julia> app.layout = html_div() do
1919
html_h1("Hello Dash"),
2020
html_div("Dash.jl: Julia interface for Dash"),
2121
dcc_graph(
@@ -29,9 +29,7 @@ julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chridd
2929
)
3030
)
3131
end
32-
end
33-
julia> handler = make_handler(app, debug = true)
34-
julia> HTTP.serve(handler, HTTP.Sockets.localhost, 8080)
32+
julia> run_server(app, "0.0.0.0", 8080)
3533
```
3634
* The `DashApp` struct represent dashboard application.
3735
* To make `DashApp` struct use `dash(layout_maker::Function, name::String; external_stylesheets::Vector{String} = Vector{String}(), url_base_pathname="/", assets_folder::String = "assets")`` where `layout_maker` is a function with signature ()::Component
@@ -47,19 +45,20 @@ __Once you have run the code to create the Dashboard, go to `http://127.0.0.1:80
4745

4846
### Basic Callback
4947
```jldoctest
50-
julia> import HTTP
48+
5149
julia> using Dash
52-
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]) do
53-
html_div() do
50+
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
51+
52+
julia> app.layout = html_div() do
5453
dcc_input(id = "my-id", value="initial value", type = "text"),
5554
html_div(id = "my-div")
5655
end
57-
end
56+
5857
julia> callback!(app, callid"my-id.value => my-div.children") do input_value
5958
"You've entered $(input_value)"
6059
end
61-
julia> handler = make_handler(app, debug = true)
62-
julia> HTTP.serve(handler, HTTP.Sockets.localhost, 8080)
60+
julia> run_server(app, "0.0.0.0", 8080)
61+
6362
```
6463
* You can make your dashboard interactive by register callbacks for changes in frontend with function ``callback!(func::Function, app::Dash, id::CallbackId)``
6564
* Inputs and outputs (and states, see below) of callback are described by struct `CallbackId` which can easily created by string macro `callid""`
@@ -68,21 +67,20 @@ julia> HTTP.serve(handler, HTTP.Sockets.localhost, 8080)
6867

6968
### States and Multiple Outputs
7069
```jldoctest
71-
julia> import HTTP
7270
julia> using Dash
73-
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]) do
74-
html_div() do
71+
julia> app = dash("Test app", external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"])
72+
73+
julia> app.layout = html_div() do
7574
dcc_input(id = "my-id", value="initial value", type = "text"),
7675
html_div(id = "my-div"),
7776
html_div(id = "my-div2")
7877
end
79-
end
78+
8079
julia> callback!(app, callid"{my-id.type} my-id.value => my-div.children, my-div2.children") do state_value, input_value
8180
"You've entered $(input_value) in input with type $(state_value)",
8281
"You've entered $(input_value)"
8382
end
84-
julia> handler = make_handler(app, debug = true)
85-
julia> HTTP.serve(handler, HTTP.Sockets.localhost, 8080)
83+
julia> run_server(app, "0.0.0.0", 8080)
8684
```
8785
* For multiple output callback must return any collection with element for each output
8886

@@ -115,11 +113,12 @@ app.layout = html.Div(children=[....])
115113

116114
* Dash.jl:
117115
```julia
118-
app = dash("Test", external_stylesheets=external_stylesheets) do
119-
html_div() do
116+
app = dash("Test", external_stylesheets=external_stylesheets)
117+
118+
app.layout = html_div() do
120119
......
121120
end
122-
end
121+
123122
```
124123
### callbacks:
125124
* python:

src/Dash.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Julia backend for [Plotly Dash](https://github.com/plotly/dash)
2727
2828
# Examples
2929
```julia
30-
import HTTP
30+
3131
using Dash
3232
app = dash("Test", external_stylesheets=["https://codepen.io/chriddyp/pen/bWLwgP.css"]) do
3333
html_div() do
@@ -55,7 +55,7 @@ callback!(app, callid"graphTitle.value => graph.figure") do value
5555
)
5656
end
5757
handle = make_handler(app, debug = true)
58-
HTTP.serve(handle, HTTP.Sockets.localhost, 8080)
58+
run_server(handle, HTTP.Sockets.localhost, 8080)
5959
```
6060
6161
# Available components

src/app.jl

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ const IdProp = Tuple{Symbol, Symbol}
33
struct CallbackId
44
state ::Vector{IdProp}
55
input ::Vector{IdProp}
6-
output ::Vector{IdProp}
7-
6+
output ::Vector{IdProp}
87
end
8+
99
CallbackId(;input ::Union{Vector{IdProp}, IdProp},
1010
output ::Union{Vector{IdProp}, IdProp},
1111
state ::Union{Vector{IdProp}, IdProp} = Vector{IdProp}()
@@ -68,14 +68,14 @@ function layout!(app::DashApp, component::Component)
6868
Components.collect_with_ids!(app.layout, app.callable_components)
6969
end
7070

71-
get_layout(app::DashApp) = Base.getfield(app, :layout).component
71+
getlayout(app::DashApp) = Base.getfield(app, :layout).component
7272

7373
function Base.setproperty!(app::DashApp, name::Symbol, value)
7474
name == :layout ? layout!(app, value) : Base.setfield!(app, name, value)
7575
end
7676

7777
function Base.getproperty(app::DashApp, name::Symbol)
78-
name == :layout ? get_layout(app) : Base.getfield(app, name)
78+
name == :layout ? getlayout(app) : Base.getfield(app, name)
7979
end
8080

8181

@@ -210,6 +210,23 @@ end
210210
211211
"""
212212
function callback!(func::Function, app::DashApp, id::CallbackId; pass_changed_props = false)
213+
214+
check_callback(func, app, id, pass_changed_props)
215+
216+
out_symbol = Symbol(output_string(id))
217+
218+
push!(app.callbacks, out_symbol => Callback(func, id, pass_changed_props))
219+
end
220+
221+
222+
function check_callback(func::Function, app::DashApp, id::CallbackId, pass_changed_props)
223+
224+
225+
226+
isempty(id.input) && error("The callback method requires that one or more properly formatted inputs are passed.")
227+
228+
length(id.output) != length(unique(id.output)) && error("One or more callback outputs have been duplicated; please confirm that all outputs are unique.")
229+
213230
for out in id.output
214231
if any(x->out in x.id.output, values(app.callbacks))
215232
error("output \"$(out)\" already registered")
@@ -219,8 +236,13 @@ function callback!(func::Function, app::DashApp, id::CallbackId; pass_changed_pr
219236
foreach(x->check_idprop(app,x), id.state)
220237
foreach(x->check_idprop(app,x), id.input)
221238
foreach(x->check_idprop(app,x), id.output)
222-
223-
out_symbol = Symbol(output_string(id))
224-
225-
push!(app.callbacks, out_symbol => Callback(func, id, pass_changed_props))
239+
240+
args_count = length(id.state) + length(id.input)
241+
pass_changed_props && (args_count+=1)
242+
243+
!hasmethod(func, NTuple{args_count, Any}) && error("Callback function don't have method with proper arguments")
244+
245+
for id_prop in id.input
246+
id_prop in id.output && error("Circular input and output arguments were found. Please verify that callback outputs are not also input arguments.")
247+
end
226248
end

test/core.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,30 @@ end
163163
return "v_$(value)"
164164
end
165165

166+
@test_throws ErrorException callback!(app, callid"{my-id.value} my-id.value => my-id.value") do value
167+
return "v_$(value)"
168+
end
169+
170+
@test_throws ErrorException callback!(app, callid"my-div.children, my-id.value => my-id.value") do value
171+
return "v_$(value)"
172+
end
173+
@test_throws ErrorException callback!(app, callid"my-id.value => my-div.children, my-id.value") do value
174+
return "v_$(value)"
175+
end
176+
177+
@test_throws ErrorException callback!(app, callid" => my-div2.title, my-id.value") do value
178+
return "v_$(value)"
179+
end
180+
181+
@test_throws ErrorException callback!(app, callid"my-id.value => my-div2.title, my-div2.title") do value
182+
return "v_$(value)"
183+
end
184+
185+
@test_throws ErrorException callback!(app, callid"my-id.value => my-div2.title") do
186+
return "v_"
187+
end
188+
189+
166190
app = dash("Test app") do
167191
html_div() do
168192
dcc_input(id = "my-id", value="initial value", type = "text"),

test/dev.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,5 @@ callback!(app, callid"""basic-interactions.relayoutData => relayout-data.childre
9696
pretty_json(relayout_data)
9797
end
9898

99-
run_server(app)
99+
run_server(app, "0.0.0.0", 8080; debug=true)
100100
end

0 commit comments

Comments
 (0)