Accessing neighbouring input

#1

Hi,

I have the following code:

searchBar model =
  div []
    [ input [ type_"text", placeholder "URL", value $INPUT_VALUE] []
     ,button [ onClick (GetData $INPUT_VALUE) ,style "display" "block" ] [ text "Get Data" ]
    ]

Is there an easy way to access the input value when the button is clicked?

On the same code I also had the following Model taken from the JSON example in the guide:

type Model
  = Failure
  | Start
  | Loading
  | Success (List Metric)

I could not figure out how to store additional Input state on it, such as

type alias Model =
  { name : String
  , password : String
  , passwordAgain : String
  }

which is given in the Form example. Maybe I have misunderstood something here?

#2

Welcome. There’s a lot to cover here but for a start: you don’t “access” the value like you would do with vanilla Javascript.

Instead you attach a onInput event to the textfield, which in turn will generate a custom Msg, say SetUrl, which as a payload will carry the most recent textfield value. In update you store such value in the model.

Then, I would suggest to attach onSubmit event the form (instead of that div you have) so you can handle the submit-by-enter behaviour. This too will generate another custom Msg, that Start you have already, which will trigger in your update the loading mentioned in your post.

I hope this will get you started!

1 Like
#3

Hey Max, welcome to the elm Community!

Your question around the update was already answered, so I’ll take on the second part:

Everything that changes during the life of an elm Application must be stored in the Model. Which means every Input must be reflected in the Model. In order to do this, you should probably change your model to something that can hold both your loading state and your username/password, because you can only have one Model.

type LoadingState
  = Failure
  | Start
  | Loading
  | Success (List Metric)

type alias Model =
  { name : String
  , password : String
  , passwordAgain : String
  , loadingState: LoadingState
  }
1 Like
#4

Thanks!

I have ended up with the following which works great for me right now:

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    SetUrl url ->
      (GotUrl url, Cmd.none)
    GetData url->
      (Loading, getData url)

    GotData result ->
      case result of
        Ok data ->
          (Success data, Cmd.none)
        Err _ ->
          (Failure, Cmd.none)


-- VIEW


view : Model -> Html Msg
view model =
  div []
    [ searchBar model
    , viewData model
    ]

searchBar: Model -> Html Msg
searchBar model =
  case model of
    GotUrl url ->
      div []
        [ input [ type_"text", placeholder "URL", value url, onInput SetUrl] []
         ,button [ onClick (GetData url) ,style "display" "block" ] [ text "Get Data" ]
        ]
    _ ->
      div []
        [ input [ type_"text", placeholder "URL", value "", onInput SetUrl] []
        ]

1 Like
#5

Hi brasilikum,

thanks! This was the puzzle piece I was missing.
Extracting the loading state like this makes it much clearer for me.

After applying this my code now looks like this:

type LoadingState
  = Failure
  | Start
  | Loading
  | Success (List Metric)
  | GotUrl String

type alias Model =
  {
    loadingState: LoadingState,
    url: String
  }


init : () -> (Model, Cmd Msg)
init _ =
  (Model Start "", Cmd.none)



-- UPDATE


type Msg
  = GetData String
  | GotData (Result Http.Error MetricCollection)
  | SetUrl String


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    SetUrl url ->
      ({model | url = url}, Cmd.none)
    GetData url->
      ({model | loadingState = Loading}, getData url)

    GotData result ->
      case result of
        Ok data ->
          ({model | loadingState = Success data}, Cmd.none)

        Err _ ->
          ({model | loadingState = Failure}, Cmd.none)

closed #6

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.