簡體   English   中英

如何在子組件之類的子組件中封裝邏輯?

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

我試圖了解如何使用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

謝謝@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 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM