Обновление поля по имени Dynami c - PullRequest
1 голос
/ 06 февраля 2020

Я хотел бы обновить поле, используя динамическое c имя поля. В моем примере у меня есть два div с атрибутом contenteditable. На событии blur мне нужно обновить модель (каждый div обновляет свое поле).

module UpdateTest exposing (main)

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (on)
import Json.Decode as Json
import Debug

type alias Model =
    { field1 : String
    , field2 : String
    }

type Msg = Update String String

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , subscriptions = subscriptions
        , view = view
        , update = update
        }

init : () -> ( Model, Cmd Msg )
init () =
    ( { field1 = "value 1", field2 = "value2" }, Cmd.none )

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

view : Model -> Html Msg
view model =
    div []
        [ div [ contenteditable True, attribute "data-id" "field1", onBlur (Update "field1") ] [ text model.field1 ]
        , div [ contenteditable True, attribute "data-id" "field2", onBlur (Update "field2") ] [ text model.field2 ]
        , div [ id "fiedl11" ] [ text model.field1 ]
        , div [ id "fiedl12" ] [ text model.field2 ]
        ]

onBlur : (String -> msg) -> Attribute msg
onBlur tagger =
    on "blur" (Json.map tagger targetTextContent) 

targetTextContent : Json.Decoder String
targetTextContent =
  Json.at ["target", "textContent"] Json.string

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Update id text ->
            ({model | id = Debug.log id text }, Cmd.none) -- here the issue

Как вы думаете, есть что-то лучше?

РЕДАКТИРОВАТЬ:

Я написал более простой пример, чем реальный, думая, что было бы лучше понять проблему , Тем не менее, количество полей очень велико и динамически растет благодаря действиям пользователя. Структура действительно более сложная, поля могут быть вложенными (4 уровня), и они упорядочены. Вы можете подумать, что это модель для текстового документа с листами, столбцами, разделами и абзацами (все эти элементы с идентификатором).

РЕДАКТИРОВАТЬ 2: Может быть, было бы более интересно использовать путь к поле внутри структуры модели для обновления его значения. Вместо использования его идентификатора.

Ответы [ 2 ]

5 голосов
/ 07 февраля 2020

Elm является языком со статической типизацией, поэтому нет способа обратиться к полю записи, используя строковое значение, так как это может привести к ошибкам типа во время выполнения.

В Elm также отсутствует какой-либо вид интроспекции / отражения во время выполнения, чтобы вы не могли получить информацию о типе во время выполнения, которая позволила бы вам автоматически сопоставлять строку с именем поля записи.

Вам нужно будет сделать следующее:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Update id text -> case id of
            "field1" -> ({model | field1 = text }, Cmd.none)
            "field2" -> ({model | field2 = text }, Cmd.none)
4 голосов
/ 07 февраля 2020

Ваш пример, кажется, не иллюстрирует вашу реальную проблему, но из этого, нашего предыдущего разговора и комментария к ответу @ Jessta, я предполагаю, что на самом деле вы хотите использовать Dict для отслеживания вашей поля. Если это так, сначала нам нужно изменить модель:

type alias Model =
    { fields : Dict String String }

. В качестве ключа используется идентификатор String, а в качестве значения используется только текст String. Если вам нужно сохранить другую информацию, вам придется заменить последний String на тип вашей модели поля.

Затем нам нужно инициализировать модель. Мы делаем это с той же информацией, которую вы имели в своем примере, но теперь она содержится в Dict:

init : () -> ( Model, Cmd Msg )
init () =
    ( { fields =
            Dict.fromList
                [ ( "field1", "value1" )
                , ( "field2", "value2" )
                ]
      }
    , Cmd.none
    )

. Рендеринг представления выполняется путем преобразования Dict в List, а затем сопоставляя его с Html элементами:

view : Model -> Html Msg
view model =
    div []
        (model.fields
            |> Dict.toList
            |> List.map
                (\( id, value ) ->
                    div
                        [ contenteditable True
                        , attribute "data-id" id
                        , onBlur (Update id)
                        ]
                        [ text value ]
                )
        )

Для краткости я пропустил два div "display", так как они казались ненужными, но добавление их в конце просто требует сделать это снова, но с другим рендером. функция. И если вам действительно нужно, чтобы они были братьями и сестрами contenteditable s, просто объедините два результирующих списка элементов.

И, наконец, чтобы обновить модель поля, мы просто используем Dict.update с id и функция обновления поля:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Update id text ->
            ( { model
                | fields =
                    Dict.update id
                        (\_ -> Just text)
                        model.fields
              }
            , Cmd.none
            )

Вот и все. Я надеюсь, что понял проблему достаточно хорошо, чтобы это помогло. Если нет, я думаю, вам просто придется попытаться объяснить это снова.

...