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 −
The syntax for defining a subscription is given below −
type Sub msg
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 −
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"
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 }
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
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 ]
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. |
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
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 }
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 −