Как включить / отключить отображение элемента в Fable?

Я хочу иметь тривиальное отображение / скрытие поведения контента в Fable. Нечто подобное.

function myFunction() {
  var x = document.getElementById("myDIV");
  if (x.style.display === "none") {
    x.style.display = "block";
  } else {
    x.style.display = "none";
  }
}
<button onclick="myFunction()">Click Me</button>

<div id="myDIV">
  This is my DIV element.
</div>

Каков наиболее адекватный способ сделать это?

2 ответа

Решение

Это гораздо больший вопрос, чем вы думаете, потому что под ним стоит вопрос "как мне лучше организовать мое приложение 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#. Функция обновления принимает модель и сообщение в качестве двух параметров и возвращает новую модель. Функция view принимает модель и функцию "dispatch" в качестве двух параметров (функция "dispatch" будет предоставлена ​​Elmish, вам не нужно ее писать) и возвращает абстрактное дерево HTML-подобных элементов. Затем Elmish передает эти элементы чему-то вроде React для фактического обновления DOM. (По сути, React будет различать "старое" дерево DOM и "новое" дерево DOM, в соответствии с процессом, очень похожим на описанный здесь для Elm).

Все это немного много, поэтому давайте рассмотрим простой пример, который просто переключает видимость div, ВНИМАНИЕ: Я не проверял это, просто набрал его в поле ответа Stack Overflow. Там могут быть ошибки в коде ниже; это предназначено как иллюстративный пример, а не рабочий пример. (ОЧЕНЬ подробный пример работы Elmish в действии см. По https://mangelmaxime.github.io/fulma-demo/ - хотя обратите внимание, что этот пример имеет продвинутую архитектуру, включающую несколько "слоев" иерархии родитель-потомок в данных модель, и может быть трудно обернуть ваш взгляд на первый взгляд).

Давайте начнем с определения вашей модели данных. Поскольку это тривиальный пример, модель аналогично тривиальна; на самом деле настолько тривиальным, что я собираюсь добавить к нему некоторые дополнительные данные, чтобы иметь смысл использовать тип записи вместо одного bool:

type Model = { Visible: bool;
               DataNotAppearingInThisFilm: int }

(Более продвинутая модель может включать Map<string, bool> тип, чтобы отслеживать видимый статус нескольких divс).

Тип сообщения также тривиален, поэтому, чтобы сделать его немного интереснее, мы допустим сообщения "Показать", "Скрыть" и "Переключить":

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"
        ]

Моя лучшая попытка - тупо перевести JavaScript по ссылке в вопросе:

module Markup

open System
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Import

let toggleButton text content =
    let id = Guid.NewGuid().ToString()

    let toggle _ = 
        let contentElement = Browser.document.getElementById id
        let display = if contentElement.style.display = "none" then "block" else "none"
        contentElement.style.display <- display

    div [] 
        [
            button [ OnClick toggle ] [ str text ]
            div [ Id id ] [ str content ]
        ]

Быть использованным как Markup.toggleButton "title" "a lot of content"

Другие вопросы по тегам