Есть хороший шаблон для работы с большим количеством полей ввода в elm? - PullRequest
3 голосов
/ 18 марта 2019

Есть ли в elm шаблон, позволяющий избежать написания большого количества сообщений только для обновления отдельных полей дочерних элементов вашей модели?

В настоящее время я заканчиваю с кодом, как показано ниже, с сообщением для каждого изменяемого входа и затем с кучей логики обновления для каждого поля. То, что я хотел бы сделать, это иметь сообщение типа AChanged, которое обрабатывает все изменения в любом свойстве A. Либо путем обновления записи в функции, которая генерирует сообщение, либо путем передачи имени поля и последующего использования его для непосредственного выполнения обновите запись, как вы могли бы в Javascript.

module Main exposing (Model)

import Browser exposing (Document, UrlRequest)
import Browser.Navigation as Nav exposing (Key)
import Html exposing (div, input)
import Html.Events exposing (onInput)
import Url exposing (Url)


type alias A =
    { a : String
    , b : String
    , c : String
    , d : String
    }


type alias B =
    { e : String
    , f : String
    , g : String
    , h : String
    }


type alias Model =
    { key : Nav.Key
    , url : Url.Url
    , a : A
    , b : B
    }


type Msg
    = UrlChanged Url.Url
    | LinkClicked Browser.UrlRequest
    | AaChanged String
    | AbChanged String
    | AcChanged String
    | AdChanged String
    | BeChanged String
    | BfChanged String
    | BgChanged String
    | BhChanged String


init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flag url key =
    ( Model key url (A "" "" "" "") (B "" "" "" ""), Cmd.none )


subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none


view : Model -> Document msg
view model =
    { title = "Mister Mandarin"
    , body =
        div
            [ input [ onInput AaChanged ] []
            , input [ onInput AbChanged ] []
            , input [ onInput AcChanged ] []
            , input [ onInput AdChanged ] []
            , input [ onInput BeChanged ] []
            , input [ onInput BfChanged ] []
            , input [ onInput BgChanged ] []
            , input [ onInput BhChanged ] []
            ]
            []
    }


update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
    case msg of
        LinkClicked urlRequest ->
            case urlRequest of
                Browser.Internal url ->
                    ( model, Nav.pushUrl model.key (Url.toString url) )

                Browser.External href ->
                    ( model, Nav.load href )

        UrlChanged url ->
            ( { model | url = url }
            , Cmd.none
            )

        AaChanged value ->
            let
                a =
                    model.a

                newA =
                    { a | a = value }
            in
            ( { model | a = newA }, Cmd.none )

        AbChanged value ->
            let
                a =
                    model.a

                newA =
                    { a | b = value }
            in
            ( { model | a = newA }, Cmd.none )

        AcChanged value ->
            let
                a =
                    model.a

                newA =
                    { a | c = value }
            in
            ( { model | a = newA }, Cmd.none )

        AdChanged value ->
            let
                a =
                    model.a

                newA =
                    { a | d = value }
            in
            ( { model | a = newA }, Cmd.none )

        BeChanged value ->
            let
                b =
                    model.b

                newB =
                    { b | e = value }
            in
            ( { model | b = newB }, Cmd.none )

        BfChanged value ->
            let
                b =
                    model.b

                newB =
                    { b | f = value }
            in
            ( { model | b = newB }, Cmd.none )

        BgChanged value ->
            let
                b =
                    model.b

                newB =
                    { b | g = value }
            in
            ( { model | b = newB }, Cmd.none )

        BhChanged value ->
            let
                b =
                    model.b

                newB =
                    { b | h = value }
            in
            ( { model | b = newB }, Cmd.none )


main : Program () Model Msg
main =
    Browser.application
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        , onUrlChange = UrlChanged
        , onUrlRequest = LinkClicked
        }

1 Ответ

4 голосов
/ 18 марта 2019

Я выбрал два совершенно разных подхода к этой проблеме. Тот, который дает вам максимальный контроль (хотя и помогает избавиться от многословия), - это переместить вашу логику с Update на View с обобщенным Msg. Что-то вроде: UpdateForm (String -> Model) или UpdateForm (String -> FormModel).

Другой подход - вообще отказаться от сохранения состояния ввода в вашей модели. Недостатком этого является то, что вы не можете выполнять такие вещи, как инициализация ваших входных данных или их простая очистка. Но это замечательно как быстрый и грязный подход к получению основных форм. В этом методе вы используете тот факт, что входные элементы с атрибутом name становятся свойствами родительского form element 1 . Вы можете прикрепить декодер к форме onSubmit и получить значение через Decode.at ["ab", "value"].

1 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-name

...