Это гораздо больший вопрос, чем вы думаете, потому что под ним стоит вопрос «как мне лучше организовать мое приложение Fable»?Подход, использованный вами в ответе, примерно параллелен использованию JQuery в приложении Javascript: вы пишете код, который напрямую манипулирует DOM для достижения желаемого результата.Если вы выбрали именно этот архитектурный стиль - и для действительно простых приложений - это отличный выбор, - ваш ответ не может быть улучшен.Единственный раз, когда подход «манипулировать DOM напрямую» наталкивается на проблемы, это если ваше приложение становится больше, чем тривиальный размер.На этом этапе вам понадобится лучшая архитектура.Вскоре я порекомендую вам лучшую архитектуру, но сначала позвольте мне порекомендовать небольшую настройку вашего кода.Перемещение функции toggle
за пределы toggleButton
сделает ее более универсальной:
module Markup
open System
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Import
let toggleBlockElem id _ =
let contentElement = Browser.document.getElementById id
let display = if contentElement.style.display = "none" then "block" else "none"
contentElement.style.display <- display
let toggleButton text content =
let id = Guid.NewGuid().ToString()
div []
[
button [ OnClick (toggleBlockElem id) ] [ str text ]
div [ Id id ] [ str content ]
]
Это позволит вам повторно использовать эту функцию в таких функциях, как toggleLink
или toggleCheckbox
, если вы нуждаетесь в них.
Теперь я упоминал ранее, что рекомендую другую архитектуру, если ваше веб-приложение станет большим.Я рекомендую архитектуру Elmish .Он основан на архитектуре, используемой в Elm ;если вы не знакомы с ним, основная идея похожа на React.Это модель / обновление / просмотр архитектуры.Модель является неизменной структурой данных.Также определен тип сообщения ;в Elmish это, вероятно, дискриминационный союз F #.Функция update принимает модель и сообщение в качестве двух параметров и возвращает новую модель.Функция view принимает модель и функцию " dispatch " в качестве двух параметров (функция "dispatch" будет предоставлена Elmish, вам не нужно ее писать) ивозвращает абстрактное дерево HTML-подобных элементов.Затем Elmish передает эти элементы чему-то вроде React для фактического обновления DOM.(Реакция, по сути, будет отличать «старое» дерево DOM и «новое» дерево DOM, в соответствии с процессом, очень похожим на описанный здесь для Elm).
Все, что естьнемного много, чтобы принять, поэтому давайте посмотрим на простой пример, который просто переключает видимость div
. ВНИМАНИЕ: Я не проверял это, просто набрал его в поле ответа переполнения стека.Там могут быть ошибки в коде ниже;это предназначено как иллюстративный пример, а не рабочий пример.(ОЧЕНЬ подробный рабочий пример Elmish в действии см. https://mangelmaxime.github.io/fulma-demo/ - хотя обратите внимание, что этот пример имеет продвинутую архитектуру, включающую несколько «слоев» иерархии родитель-потомок в модели данных, и его может быть сложно обернутьс первого взгляда).
Начнем с определения модели данных.Поскольку это тривиальный пример, модель аналогично тривиальна;на самом деле настолько тривиально, что я собираюсь добавить к нему некоторые дополнительные данные, чтобы иметь смысл использовать тип записи вместо одного bool:
type Model = { Visible: bool;
DataNotAppearingInThisFilm: int }
(более продвинутая модель можетзадействовать тип Map<string, bool>
для отслеживания видимого состояния нескольких div
с.
Тип сообщения также тривиален, поэтому, чтобы сделать его немного интереснее, мы разрешим "Показать", «Hide» и «Toggle» сообщения:
type Msg =
| Show
| Hide
| Toggle
(более продвинутая версия будет Show of string
и т. Д. С передачей идентификатора div для отображения).
Функция update
также проста:
let update msg model =
match msg with
| Show -> { model with Visible = true }
| Hide -> { model with Visible = false }
| Toggle -> { model with Visible = not model.Visible }
Наконец, функция view
будет выглядеть следующим образом:
let view model dispatch =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str (if model.Visible then "Hide" else "Show") ]
div [ Display (if model.Visible then "block" else "none") ] [ str "content" ]
]
Или потянув toggleButton
в свою собственную функцию, чтобы я мог показать, как она будет работать:
let toggleButton dispatch text content =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str text ]
div [ Display (if model.Visible then "block" else "none") ] [ str content ]
]
let view model dispatch =
div []
[
str "Other elements might go here"
toggleButton dispatch (if model.Visible then "Hide" else "Show") "content"
]
Обратите внимание, как мне нужно было передать функцию dispatch
в качестве параметра в toggleButton
, так что toggleButton
будетв состоянии построить правильное поведение для его OnClick
.
Соединение всего этого будет выглядеть так:
open ALotOfModules
type Model = { Visible: bool;
DataNotAppearingInThisFilm: int }
type Msg =
| Show
| Hide
| Toggle
let update msg model =
match msg with
| Show -> { model with Visible = true }
| Hide -> { model with Visible = false }
| Toggle -> { model with Visible = not model.Visible }
let toggleButton dispatch text content =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str text ]
div [ Display (if model.Visible then "block" else "none") ] [ str content ]
]
let view model dispatch =
div []
[
str "Other elements might go here"
toggleButton dispatch (if model.Visible then "Hide" else "Show") "content"
]