Документация Engee

Stipple

    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)

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
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__"
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
function watch

Абстрактная функция. Может использоваться плагинами для определения пользовательских функций наблюдения Vue.js.

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]
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
  }
\"""
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
  }
\"""
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() }
}")
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() }
}")
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. Разумеется, они не должны пересекаться с существующими полями модели.

function register_components(model::Type{M}, keysvals::AbstractVector) where {M<:ReactiveModel}

Вспомогательная функция для добавления компонентов Vue, которые необходимо зарегистрировать в приложении Vue.js. Обычно требуется для регистрации компонентов, предоставляемых плагинами Stipple.

Пример

Stipple.register_components(HelloPie, StippleCharts.COMPONENTS)
function components(m::Type{M})::String where {M<:ReactiveModel}
function components(app::M)::String where {M<:ReactiveModel}

Представление компонентов Vue.js, зарегистрированных для ReactiveModel M, в формате JSON.

setindex_withoutwatchers!(field::Reactive, val; notify=(x)->true)
setindex_withoutwatchers!(field::Reactive, val, keys::Int...; notify=(x)->true)

Изменяет содержимое поля Reactive, не активируя прослушиватели. Если указаны ключи, не активируются только соответствующие прослушиватели.

setfield_withoutwatchers!(app::ReactiveModel, field::Symmbol, val; notify=(x)->true)
setfield_withoutwatchers!(app::ReactiveModel, field::Symmbol, val, keys...; notify=(x)->true)

Изменяет поле ReactiveModel, не активируя прослушиватели. Если указаны ключи, не активируются только соответствующие прослушиватели.

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)
function setup(model::M, channel = Genie.config.webchannels_default_route)::M where {M<:ReactiveModel}

Настраивает реактивные обработчики для реактивных свойств модели. Вызывается автоматически.

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.

function rendering_mappings(mappings = Dict{String,String})

Регистрирует дополнительные mappings как сопоставления свойств Julia и Vue (например, foobar и foo-bar).

function julia_to_vue(field, mapping_keys = mapping_keys())

Преобразует имена Julia в имена Vue (например, foobar в foo-bar).

function parse_jsfunction(s::AbstractString)

Проверяет, является ли строка допустимой функцией js, и возвращает словарь Dict, на основе которого функция восстановления (reviver) в бэкенде может создать функцию.

function replace_jsfunction!(js::Union{Dict, JSONText})

Заменяет все значения JSONText, содержащие допустимую функцию js, словарем Dict, который кодирует функцию для восстановления. Для переменных JSONText инкапсулирует словарь в JSONText, чтобы сделать тип функции стабильным.

Заменяет все значения JSONText в копии входных данных; см. описание функции replace_jsfunction!.

function deps(channel::String = Genie.config.webchannels_default_route)

Выводит HTML-код, необходимый для внедрения зависимостей на странице (теги

Создает выражение 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>"
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
@js_str -> JSONText

Создает JSONText, например js"button=false", без интерполяции и отмены экранирования (за исключением кавычек ", которые по-прежнему должны экранироваться). Чтобы кавычки " не экранировались, можно сделать следующее: js"""alert("Hello World")""".

@kwredef(expr)

Вспомогательная функция для этапа разработки, которая является однозначной заменой @kwdef, но позволяет переопределять структуру.

На внутреннем уровне она определяет новую структуру с добавлением номера к имени исходной структуры и присваивает эту структуру переменной с именем исходной структуры.

Stipple.@kwdef

Вспомогательная функция для определения модели, которая действует как однозначная замена Base.@kwdef.

При Genie.Configuration.isprod() == true этот макрос вызывает @kwredef и позволяет переопределять модели. В противном случае он вызывает Base.@kwdef.