Elm + Components = <3

How to integrate Elm in your current stack?

Tech Lead @ Meetic

k.lebrun@meetic-corp.com

kevinlebrun

Elm

Why Elm?

  • Functional language that compiles to JavaScript
  • A simple language
  • No `null`

  • No runtime errors
  • Architecture / Framework

One-way data flow

type alias Model = Int
type alias Model = { value = 0 }

Model

view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Decrement ] [ text "-" ]
        , span [] [ text (toString model) ]
        , button [ onClick Increment ] [ text "+" ]
        ]

View

type Msg = Increment | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            model + 1

        Decrement ->
            model - 1

Update

main : Program Never
main =
    beginnerProgram
        { model = 0
        , view = view
        , update = update
        }

Main

Embed

var node = document.getElementById('counter');
Elm.Counter.embed(node);

Hello World!

Friendly error messages

Interop

How to communicate with the outside world?

Subscribe to changes

var node = document.getElementById('counter');
var app = Elm.Counter.embed(node);

app.ports.change.subscribe(function (value) {
    console.log(value);
});
port change : Model -> Cmd msg

Dispatch changes

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Increment ->
            ( model + 1, change model + 1 )

        Decrement ->
            ( model - 1, change model - 1 )

Send messages

var node = document.getElementById('counter');
var app = Elm.Counter.embed(node);

app.ports.set.send(42);
port set : (Model -> msg) -> Sub msg

Accept external messages

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of     
        Set value ->
            ( value, change value )

Set initial values

var node = document.getElementById('counter');
Elm.Counter.embed(node, { value: 42 });

Set initial values

init : { a | value : Int } -> ( Model, Cmd Msg )
init flags =
    ( flags.value, Cmd.none )

localStorage Use Case

var app = Elm.Main.fullscreen(localStorage.session || null);

app.ports.storeSession.subscribe(function(session) {
    localStorage.session = session;
});

window.addEventListener("storage", function(event) {
    if (event.storageArea === localStorage 
            && event.key === "session") {
        app.ports.onSessionChange.send(event.newValue);
    }
}, false);

Web Component

HTML APIs

  • Custom Element
  • Shadow DOM
  • HTML Templates
  • HTML Imports

Polymer

Fancy buttons

paperCard = node "paper-card"

paperButton = node "paper-button"

heading = attribute "heading"

cardContent = div [ class "card-content" ]

cardActions = div [ class "card-actions" ]

view : Model -> Html Msg
view model =
    div []
        [ paperCard [ heading "Hello World!" ]
            [ cardContent [ text (toString model) ]
            , cardActions
                [ paperButton [ onClick Decrement ] [ text "Decrement" ]
                , paperButton [ onClick Increment ] [ text "Increment" ]
                ]
            ]
        ]

Fancy counter

Custom Event Handler

detailValue =
    Json.at [ "detail", "value" ] Json.int


onValueChanged tagger =
    on "value-changed" <| Json.map tagger detailValue

Conclusion

Q/A

github.com/kevinlebrun/elm-polymer

Made with Slides.com