简体   繁体   English

如何在子组件之类的子组件中封装逻辑?

[英]How to encapsulate logic within children like component frameworks?

I'm trying to understand how to create reusable components using the Elmish architecture within F# Bolero by WebSharper (eg a reusable validated form input). 我试图了解如何使用WebSharper在F#Bolero中使用Elmish体系结构创建可重用组件(例如,可重用的经过验证的表单输入)。 From all of the examples I've seen, the top level Parent must handle all messages/updates and logic, while children are simply for views. 从我看到的所有示例中,顶层的Parent必须处理所有消息/更新和逻辑,而子级仅用于视图。 I'm wondering if there's a way around this, whether by having a child handle its own state + messages, and propagating certain messages to the parent (which I've attempted in code below), or if there's another design to handle this. 我想知道是否有办法解决这个问题,是否要让孩子处理自己的状态+消息,并将某些消息传播给父对象(我在下面的代码中尝试过),或者是否有其他设计来处理这个问题。

In my specific case, I'm trying to create a form input component for a users name that validates neither field is empty. 在我的特定情况下,我试图为用户名创建一个表单输入组件,以验证两个字段都不为空。 I don't like the idea of having a parent handle updating the individual fields FirstName and LastName, it should only care about picking up the Submit message. 我不喜欢让父母处理更新各个字段FirstName和LastName的想法,它只关心接收Submit消息。 Handling every message a child produces would results in a ton of boilerplate if you use the child more than once 如果您多次使用孩子,处理孩子产生的每条消息都会产生大量的样板

Note: The code I've provided does not compile as I'm struggling to understand how to implement my intended design 注意:我提供的代码无法编译,因为我正在努力了解如何实现预期的设计

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

Thank you @rmunn and @hvester for the references, it helped me get a better understanding of Elmish and was able to come up with a solution. 谢谢@rmunn和@hvester提供的参考,它帮助我更好地理解Elmish,并且能够提出解决方案。 As a reference for anyone else who may stumble across this, here is the solution. 对于可能偶然发现此问题的其他人,这里提供了解决方案。 InternalMessage does not need to private, it just hides those cases from the main program's update function so one can easily see which messages they need to handle. InternalMessage不需要私有,它只是在主程序的更新功能中隐藏了这些情况,因此人们可以轻松地看到它们需要处理哪些消息。 If it is public though, compiler will give an error if you try to match on an InternalMessage case without first unwrapping the Message into an InternalMessage (so the programmer still easily knows which messages are internal) 如果是公开的,那么如果您尝试匹配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 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM