我试图了解如何使用WebSharper在F#Bolero中使用Elmish体系结构创建可重用组件(例如,可重用的经过验证的表单输入)。从我看到的所有示例中,顶层的Parent必须处理所有消息/更新和逻辑,而子级仅用于视图。我想知道是否有办法解决这个问题,是否需要让孩子处理自己的状态+消息,并将某些消息传播给父对象(我在下面的代码中尝试过),或者是否有另一种设计来处理这个问题。
在我的特定情况下,我试图为用户名创建一个表单输入组件,以验证两个字段都不为空。我不喜欢让父母处理更新各个字段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
答案 0 :(得分:0)
感谢@rmunn和@hvester提供参考,它帮助我更好地理解了Elmish,并提出了解决方案。对于可能偶然发现此问题的其他人,这里提供了解决方案。 InternalMessage不需要私有,它只是在主程序的更新功能中隐藏了这些情况,因此人们可以轻松地看到它们需要处理哪些消息。如果是公开的,那么如果您尝试匹配InternalMessage情况而未先将Message展开为InternalMessage的情况下,编译器将给出错误消息(因此程序员仍然可以轻松地知道哪些消息是内部消息)
module NameInput =
type Model = { FirstName : string; LastName : string }
type private InternalMessage =
| ChangeFirstName of string
| ChangeLastName of string
type Message =
| Internal of InternalMessage
| Submit of Model
let update msg model =
match msg with
| ChangeFirstName s ->
{ model with FirstName = s }
| ChangeLastName s ->
{ model with LastName = s }
type Component() =
inherit ElmishComponent<Model, Message>()
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 -> dispatch << Internal << ChangeFirstName <| unbox e.Value)
]
label [] [ text "Last Name: " ]
input [
attr.``class`` lnClass
on.change (fun e -> dispatch << Internal << ChangeLastName <| unbox e.Value)
]
button [ on.click (fun _ -> dispatch <| Submit model) ] [ text "Submit" ]
]
type Model = { Name : NameInput.Model }
let initModel = { Name = { FirstName = ""; LastName = "" } }
type Message =
| NameInput of NameInput.Message
let update message model =
match message with
| NameInput ni ->
match ni with
| NameInput.Internal i ->
{ model with Name = model.Name |> NameInput.update i}
| NameInput.Submit n ->
{ model with Name = n }