Stipple
#
Stipple.Reactive
— Type
mutable struct Reactive{T} <: Observables.AbstractObservable{T}
Reactive
— это базовый тип для переменных, с которыми работает модель. Это объект AbstractObservable
, содержимое которого получается путем добавления []
после имени переменной Reactive
. Для удобства Reactive
можно сокращать до R
.
Есть несколько методов создания переменной Reactive:
-
r = Reactive(8)
-
r = Reactive{Float64}(8)
-
r = Reactive{Float64}(8, READONLY)
-
r = Reactive{String}("Hello", PRIVATE)
-
r = Reactive(jsfunction"console.log('Hi')", JSFUNCTION)
#
Stipple.ReactiveModel
— Type
type ReactiveModel
Абстрактный тип, наследуемый моделями Stipple. Модели Stipple используются для автоматической двусторонней синхронизации данных и обмена данными между бэкендом Julia и фронтендом JavaScript/Vue.js.
Пример
Base.@kwdef mutable struct HelloPie <: ReactiveModel
plot_options::R{PlotOptions} = PlotOptions(chart_type=:pie, chart_width=380, chart_animations_enabled=true,
stroke_show = false, labels=["Slice A", "Slice B"])
piechart::R{Vector{Int}} = [44, 55]
values::R{String} = join(piechart, ",")
end
#
Stipple.render
— Function
function render
Абстрактная функция. Должна специализироваться плагинами. Stipple
вызывает ее автоматически для сериализации типа данных Julia (соответствующего полям в экземпляре ReactiveModel
) в JavaScript/JSON. В общем случае специализированные методы должны возвращать словарь Dict
Julia, который Stipple
автоматически кодирует в формат JSON. Если для определенного типа в итоговом словаре Dict
требуется пользовательская сериализация JSON, специализируйте JSON.lower
для этого типа.
Пример
function Stipple.render(ps::PlotSeries, fieldname::Union{Symbol,Nothing} = nothing)
Dict(:name => ps.name, ps.plotdata.key => ps.plotdata.data)
end
Специализированный анализ JSON для Undefined
JSON.lower(x::Undefined) = "__undefined__"
#
Stipple.update!
— Function
function update! :: {M<:ReactiveModel}
Абстрактная функция, используемая для обновления значений полей в ReactiveModel
на основе данных из фронтенда. Может специализироваться для определенных типов, но обычно это не требуется. Если эта функция специализируется, она должна возвращать обновленный экземпляр ReactiveModel
, переданный в качестве первого параметра.
Пример
function update!(model::M, field::Any, newval::T, oldval::T)::M where {T,M<:ReactiveModel}
setfield!(model, field, newval)
model
end
#
Stipple.watch
— Function
function watch
Абстрактная функция. Может использоваться плагинами для определения пользовательских функций наблюдения Vue.js.
#
Stipple.js_methods
— Function
function js_methods(app::T) where {T<:ReactiveModel}
Определяет функции js для раздела methods
элемента vue. Ожидаемые типы результатов функции:
-
строка
String
с кодом JavaScript; -
пара
Pair
, состоящая из имени и кода функции; -
функция
Function
, возвращающая строку с кодом JavaScript; -
словарь
Dict
имен и кода функций; -
вектор
Vector
перечисленных выше элементов.
Пример 1
js_methods(::MyDashboard) = """
mysquare: function (x) {
return x^2
}
myadd: function (x, y) {
return x + y
}
\"""
Пример 2
js_methods(::MyDashboard) = Dict(:f => "function(x) { console.log('x: ' + x) })
Пример 3
js_greet() = :greet => "function(name) {console.log('Hello ' + name)}" js_bye() = :bye => "function() {console.log('Bye!')}" js_methods(::MyDashboard) = [js_greet, js_bye]
#
Stipple.js_computed
— Function
function js_computed(app::T) where {T<:ReactiveModel}
Определяет функции js для раздела computed
элемента vue. Эти свойства обновляются при каждом изменении значения одного из внутренних параметров. Ожидаемые типы результатов функции:
-
строка
String
с кодом JavaScript; -
пара
Pair
, состоящая из имени и кода функции; -
функция
Function
, возвращающая строку с кодом JavaScript; -
словарь
Dict
имен и кода функций; -
вектор
Vector
перечисленных выше элементов.
Пример
js_computed(app::MyDashboard) = """
fullName: function () {
return this.firstName + ' ' + this.lastName
}
\"""
#
Stipple.js_watch
— Function
function js_watch(app::T) where {T<:ReactiveModel}
Определяет функции js для раздела watch
элемента vue. Эти функции вызываются при каждом изменении соответствующего свойства. Ожидаемые типы результатов функции:
-
строка
String
с кодом JavaScript; -
пара
Pair
, состоящая из имени и кода функции; -
функция
Function
, возвращающая строку с кодом JavaScript; -
словарь
Dict
имен и кода функций; -
вектор
Vector
перечисленных выше элементов.
Пример
Обновляет fullName
при каждом изменении firstName
или lastName
.
js_watch(app::MyDashboard) = """
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
\"""
#
Stipple.js_created
— Function
function js_created(app::T)::Union{Function, String, Vector} where {T<:ReactiveModel}
Определяет операторы js для раздела created
элемента vue.
Возможные типы результатов функции:
-
строка
String
с кодом JavaScript; -
функция
Function
, возвращающая строку с кодом JavaScript; -
вектор
Vector
перечисленных выше элементов.
Пример 1
js_created(app::MyDashboard) = """
if (this.cameraon) { startcamera() }
\"""
Пример 2
startcamera() = "if (this.cameraon) { startcamera() }"
stopcamera() = "if (this.cameraon) { stopcamera() }"
js_created(app::MyDashboard) = [startcamera, stopcamera]
Проверить результат можно следующим образом.
julia> render(MyApp())[:created] JSONText("function(){ if (this.cameraon) { startcamera() } if (this.cameraon) { stopcamera() } }")
#
Stipple.js_mounted
— Function
function js_mounted(app::T)::Union{Function, String, Vector} where {T<:ReactiveModel}
Определяет операторы js для раздела mounted
элемента vue.
Возможные типы результатов функции:
-
строка
String
с кодом JavaScript; -
функция
Function
, возвращающая строку с кодом JavaScript; -
вектор
Vector
перечисленных выше элементов.
Пример 1
js_mounted(app::MyDashboard) = """
if (this.cameraon) { startcamera() }
\"""
Пример 2
startcamera() = "if (this.cameraon) { startcamera() }"
stopcamera() = "if (this.cameraon) { stopcamera() }"
js_mounted(app::MyDashboard) = [startcamera, stopcamera]
Проверить результат можно следующим образом.
julia> render(MyApp())[:mounted] JSONText("function(){ if (this.cameraon) { startcamera() } if (this.cameraon) { stopcamera() } }")
#
Stipple.client_data
— Function
function client_data(app::T)::String where {T<:ReactiveModel}
Определяет дополнительные данные, которые будут доступны только браузеру.
Эта функция предназначена для хранения изменчивых данных, например данных формы, которые сначала должны пройти проверку. Для использования данных, скорее всего, также потребуется определить js_methods
.
Пример
import Stipple.client_data
client_data(m::Example) = client_data(client_name = js"null", client_age = js"null", accept = false)
определяет дополнительные поля client_name
, client_age
и accept
для модели Example
. Разумеется, они не должны пересекаться с существующими полями модели.
#
Stipple.register_components
— Function
function register_components(model::Type{M}, keysvals::AbstractVector) where {M<:ReactiveModel}
Вспомогательная функция для добавления компонентов Vue, которые необходимо зарегистрировать в приложении Vue.js. Обычно требуется для регистрации компонентов, предоставляемых плагинами Stipple.
Пример
Stipple.register_components(HelloPie, StippleCharts.COMPONENTS)
#
Stipple.components
— Function
function components(m::Type{M})::String where {M<:ReactiveModel}
function components(app::M)::String where {M<:ReactiveModel}
Представление компонентов Vue.js, зарегистрированных для ReactiveModel
M
, в формате JSON.
#
Stipple.setindex_withoutwatchers!
— Function
setindex_withoutwatchers!(field::Reactive, val; notify=(x)->true)
setindex_withoutwatchers!(field::Reactive, val, keys::Int...; notify=(x)->true)
Изменяет содержимое поля Reactive, не активируя прослушиватели. Если указаны ключи, не активируются только соответствующие прослушиватели.
#
Stipple.setfield_withoutwatchers!
— Function
setfield_withoutwatchers!(app::ReactiveModel, field::Symmbol, val; notify=(x)->true)
setfield_withoutwatchers!(app::ReactiveModel, field::Symmbol, val, keys...; notify=(x)->true)
Изменяет поле ReactiveModel, не активируя прослушиватели. Если указаны ключи, не активируются только соответствующие прослушиватели.
#
Stipple.init
— Function
function init(::Type{M};
vue_app_name::S = Stipple.Elements.root(M),
endpoint::S = vue_app_name,
channel::Union{Any,Nothing} = nothing,
debounce::Int = JS_DEBOUNCE_TIME,
transport::Module = Genie.WebChannels,
core_theme::Bool = true)::M where {M<:ReactiveModel, S<:AbstractString}
Инициализирует реактивность модели M
, создавая пользовательский код JavaScript для интеграции с фронтендом Vue.js и выполняя двустороннюю синхронизацию данных между бэкендом и фронтендом. Возвращает экземпляр модели.
Пример
hs_model = Stipple.init(HelloPie)
#
Stipple.setup
— Function
function setup(model::M, channel = Genie.config.webchannels_default_route)::M where {M<:ReactiveModel}
Настраивает реактивные обработчики для реактивных свойств модели. Вызывается автоматически.
#
Base.push!
— Method
Base.push!(app::M, vals::Pair{Symbol,T}; channel::String,
except::Union{Genie.WebChannels.HTTP.WebSockets.WebSocket,Nothing,UInt}) where {T,M<:ReactiveModel}
Передает полезные данные во фронтенд, транслируя vals
через channel
.
#
Stipple.rendering_mappings
— Function
function rendering_mappings(mappings = Dict{String,String})
Регистрирует дополнительные mappings
как сопоставления свойств Julia и Vue (например, foobar
и foo-bar
).
#
Stipple.julia_to_vue
— Function
function julia_to_vue(field, mapping_keys = mapping_keys())
Преобразует имена Julia в имена Vue (например, foobar
в foo-bar
).
#
Stipple.parse_jsfunction
— Function
function parse_jsfunction(s::AbstractString)
Проверяет, является ли строка допустимой функцией js, и возвращает словарь Dict
, на основе которого функция восстановления (reviver) в бэкенде может создать функцию.
#
Stipple.replace_jsfunction!
— Function
function replace_jsfunction!(js::Union{Dict, JSONText})
Заменяет все значения JSONText, содержащие допустимую функцию js, словарем Dict
, который кодирует функцию для восстановления. Для переменных JSONText инкапсулирует словарь в JSONText, чтобы сделать тип функции стабильным.
#
Stipple.replace_jsfunction
— Function
Заменяет все значения JSONText в копии входных данных; см. описание функции replace_jsfunction!
.
#
Stipple.deps
— Function
function deps(channel::String = Genie.config.webchannels_default_route)
Выводит HTML-код, необходимый для внедрения зависимостей на странице (теги
#
Stipple.@R_str
— Macro
Создает выражение JS, привязанное к полю компонента Vue. На внутреннем уровне эта функция попросту выполняет преобразование в символ, однако обеспечивает более краткую форму для создания символов с пробелами.
Пример
julia> btn("", @click("toggleFullscreen"), icon = R"is_fullscreen ? 'fullscreen_exit' : 'fullscreen'") "<q-btn label v-on:click="toggleFullscreen" :icon="is_fullscreen ? 'fullscreen_exit' : 'fullscreen'"></q-btn>"
Примечание. Для выражений, содержащих только имена переменных, рекомендуется использовать символьную нотацию.
julia> btn("", @click("toggleFullscreen"), icon = :fullscreen_icon) "<q-btn label v-on:click="toggleFullscreen" :icon="fullscreen_icon"></q-btn>"
#
Observables.on
— Function
on(f, observable::AbstractObservable; weak = false, priority=0, update=false)::ObserverFunction
Добавляет функцию f
в качестве прослушивателя observable
. При каждом задании значения observable
посредством observable[] = val
функция f
вызывается с val
.
Возвращает объект ObserverFunction
, который инкапсулирует f
и observable
и позволяет легко произвести отключение, вызвав off(observerfunction)
вместо off(f, observable)
. Если вместо этого нужно вычислить новый объект Observable
на основе старого, используйте map(f, ::Observable)
.
При значении weak = true
новое соединение удаляется немедленно, если на возвращенный объект ObserverFunction
больше нет ссылок и он удаляется сборщиком мусора. Это полезно, если какой-либо родительский объект устанавливает соединения с внешними наблюдаемыми объектами и сохраняет полученные экземпляры ObserverFunction
. Затем, как только этот родительский объект подвергается сборке мусора, слабые наблюдаемые связи автоматически удаляются.
Пример
julia> obs = Observable(0)
Observable(0)
julia> on(obs) do val
println("current value is ", val)
end
ObserverFunction defined at REPL[17]:2 operating on Observable(0)
julia> obs[] = 5;
current value is 5
Также можно задать приоритет обратного вызова, чтобы он всегда выполнялся до других или после них независимо от порядка регистрации. Обратный вызов с наивысшим приоритетом выполняется первым; значение по умолчанию равно нулю, и можно использовать весь диапазон Int. Поэтому можно сделать следующее.
julia> obs = Observable(0)
julia> on(obs; priority=-1) do x
println("Hi from first added")
end
julia> on(obs) do x
println("Hi from second added")
end
julia> obs[] = 2
Hi from second added
Hi from first added
Если задать update=true
, on немедленно вызовет f(obs[]):
julia> on(Observable(1); update=true) do x
println("hi")
end
hi
onbutton(f::Function, button::R{Bool}; async = false, weak = false)
Связывает функцию с реактивным логическим параметром, обычно представляющим кнопку в приложении. После вызова функции параметру снова присваивается значение false. Именованный аргумент async
указывает, должен ли вызов выполняться асинхронно.
Пример
onbutton(model.save_button) do
# сохраняем необходимые данные
end
#
Stipple.@js_str
— Macro
@js_str -> JSONText
Создает JSONText, например js"button=false"
, без интерполяции и отмены экранирования (за исключением кавычек "
, которые по-прежнему должны экранироваться). Чтобы кавычки "
не экранировались, можно сделать следующее: js"""alert("Hello World")"""
.
#
Stipple.@kwredef
— Macro
@kwredef(expr)
Вспомогательная функция для этапа разработки, которая является однозначной заменой @kwdef
, но позволяет переопределять структуру.
На внутреннем уровне она определяет новую структуру с добавлением номера к имени исходной структуры и присваивает эту структуру переменной с именем исходной структуры.
#
Stipple.@kwdef
— Macro
Stipple.@kwdef
Вспомогательная функция для определения модели, которая действует как однозначная замена Base.@kwdef
.
При Genie.Configuration.isprod() == true
этот макрос вызывает @kwredef
и позволяет переопределять модели. В противном случае он вызывает Base.@kwdef
.