How do I make two independent actions inside one function?

660 views Asked by At

How can I make two independent action inside one function in Elm? Is there any pattern or explicit function?

Generally speaking I'm not sure how to implement ajax data loading inside Elm Architecture.

For example I'd like to make Http.get and return modified argument function like this

fetchCustomers model =
  Http.get parseCustomers "/customers" `andThen` setCustomers
  { model | fetchingCustomers <- True }
1

There are 1 answers

7
Apanatshka On BEST ANSWER

TL;DR

You do so by returning both in a tuple. Then you split the signal from your update in foldp, put the model part in the view function and put the tasks in a port to execute. This is alluded to in the Architecture post at the end under One Last Pattern.

Longer answer

Since you link to the Elm Architecture, let me link to that too, but in particular the last part: One Last Pattern.

What you want to do here is part of the "update" of your program, where you not only update your model but also do something else on the side. Therefore you do not just return the new model, but also the extra thing you want to do (in this case an Http request):

fetchCustomers model =
  ( { model | fetchingCustomers <- True }
  , Http.get parseCustomers "/customers" `andThen` setCustomers
  )

Instead of using StartApp like the architecture page does, you can paste in the start function from the package. Now you have access to the mailbox where the actions are coming from, so you can pass it to your update so you can send your Http results there too. And you can split the tuple that you're returning from the update function to actually execute the tasks:

start app =
  let
    actions =
      Signal.mailbox Nothing

    address =
      Signal.forwardTo actions.address Just

    model =
      Signal.foldp
        (\(Just action) (model,_) -> app.update actions action model)
        -- ignore task: ^     ^^^              ^^^^^^^^:add mailbox
        app.model
        actions.signal
  in
    (Signal.map (fst >> app.view address) model, Signal.map snd model)
--  ^           ^^^^^^^     :split model:      ^^^^^^^^^^^^^^^^^^^^^^^


fetchCustomers actions model =
  ( { model | fetchingCustomers <- True }
  , Http.get parseCustomers "/customers"
    `andThen` (SetCustomers >> Signal.send actions.address)
  -- use mailbox to send Http results as an input action again
  )

-- larger update function you probably have
update actions action model = case action of
  -- ...
  SetCustomers cust -> setCustomers cust
  -- ...
  -- fetchCustomers actions model

(output, tasks) = start { model: model, view: view, update: update }

-- task execution:
port httpGets = tasks

-- output your view
main = output

You can find more examples of doing Http on the website under "tasks".