This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.
A clean convention for sending messages via ports.
version 2.0.0
license BSD3
native-modules False
elm-version 0.18.0 <= v < 0.19.0
Tag 2.0.0
Committed At 2018-04-11 02:07:03 UTC
elm-lang/core 5.1.1 <= v < 6.0.0 5.1.1

Modules

README

elm-port-message

A clean convention for sending messages via ports.

Thanks to @splodingsocks for the inspiration to not create a separate port for each Cmd/Sub. The Importance of Ports

Background

Dropping into javascript to provide functionality not yet available in Elm doesn't have to be the wild west. Instead of creating a port for every usecase, why not create a single port for each direction required per module/feature then expose functions from your port module that simply build up a PortMessage before its passed into your port.

Example Port Module (Document.elm)

We encode a string here in the payload but you can encode anything you need.

port module Document exposing (modifyTitle)

import Json.Encode
import PortMessage exposing (PortMessage)


modifyTitle : String -> Cmd a
modifyTitle title =
    PortMessage.new "ModifyTitle"
        |> PortMessage.withPayload (Json.Encode.string title)
        |> document


port document : PortMessage -> Cmd a

Example Port Module (View.elm)

None of the functions exposed from this API require any payload so the payload will simply be null in js.

port module View exposing (enterFullscreen, leaveFullscreen, toggleFullscreen)

import PortMessage exposing (PortMessage)


enterFullscreen : Cmd a
enterFullscreen =
    PortMessage.new "EnterFullscreen"
        |> view

leaveFullscreen : Cmd a
leaveFullscreen =
    PortMessage.new "LeaveFullscreen"
        |> view

toggleFullscreen : Cmd a
toggleFullscreen =
    PortMessage.new "ToggleFullscreen"
        |> view

port view : PortMessage -> Cmd a

Elm Usage

import Document
import View

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        TextInput value ->
            ({ model | text = value }, Document.modifyTitle value)

        -- Note we don't have the awkward identity `()` argument from having
        -- a port that doesn't require any additional data.
        FullscreenBtnPressed ->
            (model, View.enterFullscreen)

Javascript Usage

const app = Elm.App.fullscreen();

app.ports.document.subscribe({ tag, payload } => {
  switch (tag) {
    case 'ModifyTitle':
      document.title = payload;
      break;
  }
});

app.ports.view.subscribe({ tag, payload } => {
  // same as above but has cases for 'EnterFullscreen', 'LeaveFullscreen' and
  // 'ToggleFullscreen'
});

Ports.elm or Separate Modules?

How you structure your ports whether you decide to have a single Ports module or split it up into different modules by feature or API is entirely your choice and this package will work either way. Personally I like having separate modules for each service File.elm, Phoenix.elm, Document.elm over a single module for everything for several reasons.

  1. Sharing code between projects. It's easy to just copy a small focused port module to another project without bringing along services that are not required.

  2. The port modules can have other functionality that's not related to working with the port, for example the File module may expose load : Filename -> Cmd a where Filename is a type defined in File.elm, or maybe bytesize : File -> Int again where File is a record alias defined in File.elm.

  3. It feels more natural, compare Ports.join, Ports.joinChannel, Ports.joinPhoenixChannel or my favourite Phoenix.join

  4. Faster compile times.

Contributing

I'm happy to receive any feedback and ideas for about additional features. Any input and pull requests are very welcome and encouraged. If you'd like to help or have ideas, get in touch with me at @Hendore in the elmlang Slack!