Elm - Subscriptions


In the previous chapter, we discussed that a View interacts with other components using Commands. Similarly, a component (E.g. WebSocket) can talk to a View using Subscriptions. Subscriptions are a way that an Elm application can receive external inputs like keyboard events, timer events and WebSocket events.

The following figure explains the role of Subscriptions in an Elm application. The user interacts with an Elm application via messages. The application given uses WebSocket and it has two modes of operations −

  • Send client-side data to socket server via Command
  • Receive data anytime from the socket server via Subscription
socket server

Syntax

The syntax for defining a subscription is given below −

type Sub msg

Illustration

Let us understand subscriptions using a simple example.

In the example given below, the application sends a message to the server. The server is an echo server, which responds to the client with the same message. All the incoming messages are later displayed in a list. We will use WebSocket (wss protocol) to be able to continuously listen for messages from the server. The WebSocket will send user input to the server using Commands while it will use Subscription to receive messages from the server.

The various components of the application are given below −

Echo server

The echo server can be accessed using the wss protocol. The echo server sends back user input to the application. The code for defining an echo server is given below −

echoServer : String
echoServer =
"wss://echo.websocket.org"

Model

The Model represents user input and a list of incoming messages from the socket server. The code for defining the Model is as given below −

type alias Model =
   { input : String
   , messages : List String
   }

Messages

The message type will contain Input for taking text input from user. The Send message will be generated when user clicks the button to send message to WebSocket server. The NewMessage is used when message arrives from echo server.

type Msg
   = Input String
   | Send
   | NewMessage String

View

The application's view contains a textbox and a submit button to send user input to the server. The response from the server is displayed on the View using a div tag.

view : Model -> Html Msg
view model =
   div []
      [ input [onInput Input, value model.input] []
      , button [onClick Send] [text "Send"]
      , div [] (List.map viewMessage (List.reverse model.messages))
      ]

viewMessage : String -> Html msg
viewMessage msg =
   div [] [ text msg ]

Update

The update function takes the message and the model components. It updates the model based on the message type.

update : Msg -> Model -> (Model, Cmd Msg)
update msg {input, messages} =
   case msg of
      Input newInput ->
         (Model newInput messages, Cmd.none)

   Send ->
      (Model "" messages, WebSocket.send echoServer input)

   NewMessage str ->
      (Model input (str :: messages), Cmd.none)
Sr. No. Method Signature Description
1 WebSocket.listen listen : String -> (String -> msg) -> Sub msg Subscribes to any incoming messages on a websocket.
2 WebSocket.send send : String -> String -> Cmd msg Sends a wss request to a server address. It is important that you are also subscribed to this address with listen. If you are not, the web socket will be created to send one message and then closed.

Subscription

The subscription function takes in the model object. To receive the messages from WebSocket server, we call WebSocket.listen passing in the message as NewMessage. When a new message comes from the server, the update method is called.

subscriptions : Model -> Sub Msg
subscriptions model =
WebSocket.listen echoServer NewMessage

main

The main function is the entry point to the elm application as shown below.

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

Putting it all together

Step 1 − Create a directory,SubscriptionApp and add a file,SubscriptionDemo.elm to it.

Step 2 − Add the following contents to SubscriptionDemo.elm file −

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import WebSocket

main =
   Html.program
      { init = init
      , view = view
      , update = update
      , subscriptions = subscriptions
      }

echoServer : String
echoServer =
   "wss://echo.websocket.org"

-- MODEL

type alias Model =
   { input : String
   , messages : List String
   }

init : (Model, Cmd Msg)
init =
   (Model "" [], Cmd.none)

-- UPDATE
type Msg
   = Input String
   | Send
   | NewMessage String

update : Msg -> Model -> (Model, Cmd Msg)
update msg {input, messages} =
   case msg of
      Input newInput ->
      (Model newInput messages, Cmd.none)

   Send ->
      (Model "" messages, WebSocket.send echoServer input)

   NewMessage str ->
      (Model input (str :: messages), Cmd.none)

-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
   WebSocket.listen echoServer NewMessage

-- VIEW
view : Model -> Html Msg
view model =
   div []
      [ input [onInput Input, value model.input] []
      , button [onClick Send] [text "Send"]
      , div [] (List.map viewMessage (List.reverse model.messages))
      ]

viewMessage : String -> Html msg
viewMessage msg =
div [] [ text msg ]

Step 3 − Install the websockets package using elm package manager.

C:\Users\dell\elm\SubscriptionApp> elm-package install elm-lang/websocket

Step 4 − Build and generate index.html file as shown below.

C:\Users\dell\elm\SubscriptionApp> elm make .\SubscriptionDemo.elm

Step 5 − Upon execution, the following output will be generated −

SubscriptionApp
Advertisements