As a quick note to anyone struggling with how to perform app wide inter-component communication in elm 0.17 (e.g. anyone building a non-trivial SPA), here is one simple way to setup pubsub style communication using Elm Ports and Subscriptions.In this example, ChildOne will broadcast a message that ChildTwo is listening for.Ports.elmport module Ports exposing (..)-- dispatchSomeMessage will create a command with a
-- string payload representing an important message
-- to broadcast to listening components.port dispatchSomeMessage : String -> Cmd msg
-- receiveSomeMessage is the port which our components
-- subscribe to receive the dispatched messageport receiveSomeMessage : (String -> msg) -> Sub msgThe Ports.elm file contains all of the port specifications in the app. In this case it defines a two ports: one port for dispatching a message, and another for receiving a message. The module declaration at the top of the file must be preceded by "port".index.jsvar app = Elm.Main.fullscreen();app.ports.dispatchSomeMessage.subscribe(function(msg) {app.ports.receiveSomeMessage.send(msg);});This is where the behaviour of all ports are defined in javascript. In this case the dispatchSomeMessage function is receiving a msg argument from Elm, and then quickly sending that message back into Elm through the receiveSomeMessage port.
ChildOne.elmmodule ChildOne exposing (..)import Ports exposing (..)import Html exposing (Html, button, text)import Html.Events exposing (onClick)type alias Model = Stringtype Msg= Clicksubscriptions : Model -> Sub Msgsubscriptions model =Sub.noneupdate : Msg -> Model -> ( Model, Cmd Msg )update msg model =case msg ofClick ->( model, dispatchSomeMessage "Hello World!" )view : Model -> Html Msgview model =button [ onClick Click ] [text "Click Me"]ChildOne dispatches the message "Hello World!" through the dispatchSomeMessage port when a button in the view is clicked. The message is then routed from the dispatchSomeMessage port, directly back into the receiveSomeMessage port as defined in index.js
ChildTwo.elmmodule ChildTwo exposing (..)import Ports exposing (..)import Html exposing (Html, text)type alias Model =
{ message : String }type Msg= ChangeMessage Stringsubscriptions : Model -> Sub Msgsubscriptions model =
receiveSomeMessage ChangeMessageupdate : Msg -> Model -> ( Model, Cmd Msg )update msg model =case msg ofChangeMessage msg ->({ model | message = msg }, Cmd.none )view : Model -> Html Msgview model =text model.messageChildTwo subscribes a ChangeMessage tag to the 'receiveSomeMessage' port. When the port receives the "Hello World" message from ChildOne, ChildTwo updates its models message, and re-renders the view.
Main.elmmodule Main exposing (..)import ChildOne
import ChildTwotype alias Model ={ childOneModel : ChildOne.model, childTwoModel : ChildTwo.model}
-- ..init func. nothing special here.type Msg= ChildOneMsg ChildOne.Msg| ChildTwoMsg ChildTwo.Msgsubscriptions : Model -> Sub Msgsubscriptions model =Sub.batch[ Sub.map ChildOneMsg (ChildOne.subscriptions model. childOneModel), Sub.map ChildTwoMsg (ChildTwo.subscriptions model. childTwoModel)]
-- .. update function. nothing special here.main : Program Nevermain =Html.App.program{ init = init, update = update, view = view, subscriptions = subscriptions}
Main.elm batches the subscriptions defined in child components. That's all there is to it. Nice and clean inter-component communication.
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Just don't go componentizing every little text field or icon.
Thanks for all!
port module Events exposing (Event(..), publish, subscribe)
type Event = SomethingHappened | SomethingElseHappened Int
type alias DTO = (String, String)
-- Ports
port eventOutgoingPort : DTO -> Cmd msg
port eventIncomingPort : (DTO -> msg) -> Sub msg
-- PubSub
publish : Event -> Cmd msgpublish evt = outgoingEventPort (serialize evt)
subscribe : (Event -> msg) -> Sub msgsubscribe f = Sub.map f (incomingEventPort deserialize)
-- Serialization
serialize : Event -> DTOserialize evt = case evt of SomethingHappened -> ("SomethingHappened", "")
SomethingElseHappened int -> ("SomethingElseHappened", ... serialize int to json)
deserialize : DTO -> Eventdeserialize ( key, json ) = case key of "SomethingHappened" -> SomethingHappened
"SomethingElseHappened" -> SomethingElseHappened ... deserialize json to int
update : Msg -> Model -> ( Model, Cmd Msg )update msg model = case msg of BlahMsg -> ( initialModel, Events.publish Events.SomethingHappened )type Msg = Event Events.Event
subscriptions : Model -> Sub Msgsubscriptions model = Events.subscribe Event
type alias DTO =(String, String)
subscriptions : Model -> Sub Msgsubscriptions model =
Sub.none
update : Msg -> Model -> ( Model, Cmd Msg )update msg model =case msg of
Click ->( model, dispatchSomeMessage "Hello World!" )view : Model -> Html Msgview model =button [ onClick Click ] [text "Click Me"]
ChildOne dispatches the message "Hello World!" through the dispatchSomeMessage port when a button in the view is clicked. The message is then routed from the dispatchSomeMessage port, directly back into the receiveSomeMessage port as defined in index.js
ChildTwo.elm
module ChildTwo exposing (..)import Ports exposing (..)import Html exposing (Html, text)type alias Model =
{ message : String }type Msg= ChangeMessage String
subscriptions : Model -> Sub Msg
subscriptions model =
receiveSomeMessage ChangeMessage
update : Msg -> Model -> ( Model, Cmd Msg )update msg model =case msg of
ChangeMessage msg ->({ model | message = msg }, Cmd.none )
view : Model -> Html Msgview model =text model.message
ChildTwo subscribes a ChangeMessage tag to the 'receiveSomeMessage' port. When the port receives the "Hello World" message from ChildOne, ChildTwo updates its models message, and re-renders the view.
Main.elm
module Main exposing (..)
import ChildOne
import ChildTwotype alias Model ={ childOneModel : ChildOne.model, childTwoModel : ChildTwo.model}
-- ..init func. nothing special here.type Msg= ChildOneMsg ChildOne.Msg| ChildTwoMsg ChildTwo.Msg
subscriptions : Model -> Sub Msgsubscriptions model =
Sub.batch[ Sub.map ChildOneMsg (ChildOne.subscriptions model. childOneModel), Sub.map ChildTwoMsg (ChildTwo.subscriptions model. childTwoModel)]
-- .. update function. nothing special here.main : Program Nevermain =Html.App.program{ init = init, update = update, view = view, subscriptions = subscriptions}
When you're building something at a larger scale like a single page app, that contains various layouts, menus, & pages - which contain several components per page - you'll need components to keep your code organized, maintainable, and easy to understand.
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+unsubscribe@googlegroups.com.
Like I said, I regret posting the original solution that I did, since it was essentially circumventing the elm language. You should try to stay within elm as much as possible, unless you're truly missing a piece of functionally, such as working with files, etc.