lobanov/elm-taskport/1.0.1

NOTE: the latest version is 2.0.1

TaskPort

This module allows to invoke JavaScript functions using the Elm's Task abstraction, which is convenient for chaining multiple API calls without introducing the complexity in the model of an Elm application.

Setting up

Before TypePort can be used in Elm, it must be set up on JavaScript side. Refer to the README for comprehensive instructions.

Usage

String
-> Decoder body
-> Decoder error
-> (args -> Value)
-> args
-> Task (Error error) body

Creates a Task encapsulating an invocation of a particular asyncronous JavaScript function. This function will usually be wrapped into a more specific one, which will partially apply it providing the encoder and the decoders but curry the last parameter, so that it could invoked where necessary as a args -> Task function.

Because interop calls can fail, produced task would likely need to be piped into a Task.attempt or handled further using Task.onError.

Here is a simple example that creates a Cmd invoking a registered JavaScript function called ping and produces a message GotPong with a Result, containing either an Ok variant with a string (determined by the first decoder argument), or an Err, containing a TaskPort.Error describing what went wrong.

type Msg = GotPong (Result String String)


Task.attempt GotPong <|
  TaskPort.call "ping" Json.Decode.string Json.Decode.string Json.Encode.string "hello"

The Task abstraction allows to effectively compose chains of tasks without creating many intermediate message type, and designing the model to deal with partially completed chains. The following example shows how this might be used when working with a hypothetical 'chatty' JavaScript API, requiring to call getWidgetsCount function to obtain a number of widgets, and then call getWidgetName for each widget to obtain its name.

TaskPort.callNoArgs "getWidgetsCount" Json.Decode.int Json.Decode.string
   |> Task.andThen \count -> Task.sequence <|
    List.range 0 (count - 1) |>
    List.map Json.Encode.int |>
    List.map (TaskPort.call "getWidgetName" Json.Decode.string Json.Decode.string)

The resulting task has type Task (TaskPort.Error String) (List String), which could be attempted as a single command, which, if successful, provides a handy List String with all widget names.

callNoArgs : String -> Decoder body -> Decoder error -> Task (Error error) body

Special version of the call that reduces amount of boilerplate code required when calling JavaScript functions that don't take any parameters. It is eqivalent of passing Json.Encoder.null into the call.

TaskPort.callNoArgs "getWidgetsCount" Json.Decode.int Json.Decode.string
type Error x
= InteropError InteropError
| CallError x

A structured error describing exactly how the interop call failed. You can use this to determine the best way to react to and recover from the problem.

CallError prefix is for errors explicitly sent from the JavaScript side. The error information will be specific to the interop use case, and it should be reconsituted from a JSON payload.

InteropError prefix is for the failures of the interop mechanism itself.

= FunctionNotFound
| NotInstalled
| VersionMismatch
| CannotDecodeResponse Error String
| CannotDecodeError Error String
| RuntimeError String

Subcategory of errors indicating a failure of the interop mechanism itself. These errors are generally not receoverable, but you can use them to allow the application to fail gracefully, or at least provide useful context for debugging.

Tests

We are exposing tests suite to help test module's implementation details.

tests : Test

This is an embedded tests suite allowing to test module-internal logic. No need to use this.