Я пытаюсь понять, как создавать повторно используемые компоненты, используя архитектуру Elmish в F # Bolero от WebSharper (например, многократно используемый проверенный ввод формы). Из всех примеров, которые я видел, родитель верхнего уровня должен обрабатывать все сообщения/обновления и логику, в то время как дочерние элементы предназначены только для представлений. Мне интересно, есть ли способ обойти это, будь то то, что дочерний элемент обрабатывает свое собственное состояние + сообщения и распространяет определенные сообщения родителю (что я пытался сделать в коде ниже), или если есть другой дизайн для обработки этого.
В моем конкретном случае я пытаюсь создать компонент ввода формы для имени пользователя, который проверяет, что ни одно поле не пусто. Мне не нравится идея иметь родительский дескриптор, обновляющий отдельные поля FirstName и LastName, он должен заботиться только о получении сообщения Submit. Обработка каждого сообщения, создаваемого дочерним элементом, приведет к тонне шаблонов, если вы используете дочерний элемент более одного раза.
Примечание. Код, который я предоставил, не компилируется, так как я изо всех сил пытаюсь понять, как реализовать задуманный мной дизайн.
open Elmish
open Bolero
open Bolero.Html
module NameInput =
type Model = { FirstName : string; LastName : string }
type Message =
| ChangeFirstName of string
| ChangeLastName of string
| Submit of Model
let update model msg =
match msg with
| ChangeFirstName s ->
{ model with FirstName = s}, Cmd.none
| ChangeLastName s ->
{ model with LastName = s}, Cmd.none
| Submit m ->
m, Cmd.ofMsg (Submit m)
type Component() =
inherit ElmishComponent<Message, Model>()
let invalidField s = s <> ""
override this.View model dispatch =
let fnClass = if (invalidField model.FirstName) then "invalid" else "valid"
let lnClass = if (invalidField model.LastName) then "invalid" else "valid"
div [] [
label [] [ text "First Name: " ]
input [
attr.``class`` fnClass
on.change (fun e -> update model (ChangeFirstName (unbox e.Value)))
]
label [] [ text "Last Name: " ]
input [
attr.``class`` lnClass
on.change (fun e -> update model (ChangeLastName (unbox e.Value)))
]
button [ on.click (fun _ -> update model (Submit model)) ] [ text "Submit" ]
]
type Message =
| NameSubmitted of NameInput.Message.Submit
type Model = { UserName : NameInput.Model }
let initModel = { UserName = { FirstName = ""; LastName = "" } }
let update msg model =
match msg with
| NameSubmitted name ->
// Greet the user
{ model with UserName = name }, Cmd.none
let view model dispatch =
concat [
ecomp<NameInput.Component,_,_>
model.Username dispatch
]
type MyApp() =
inherit ProgramComponent<Model, Message>()
override this.Program =
Program.mkProgram (fun _ -> initModel, Cmd.none) update view