Elm Language
La arquitectura del olmo
Buscar..
Introducción
La forma recomendada de estructurar sus aplicaciones se denomina 'The Elm Architecture'.
El programa más simple consiste en un registro de model almacena todos los datos que pueden actualizarse, un Msg tipo de unión que define las formas en que su programa actualiza esos datos, una update función que toma el modelo y un Msg y devuelve un nuevo modelo, y una view función que toma un modelo y devuelve el HTML que mostrará su página. Cada vez que una función devuelve un Msg , el tiempo de ejecución de Elm lo utiliza para actualizar la página.
Programa de principiante
Html tiene un programa para beginnerProgram principalmente para fines de aprendizaje.
beginnerProgram no es capaz de manejar suscripciones o ejecutar comandos.
Solo es capaz de manejar la entrada del usuario de los eventos DOM.
Solo requiere una view para representar el model y una función de update para manejar los cambios de estado.
Ejemplo
Considere este ejemplo mínimo de beginnerProgram .
El model en este ejemplo consiste en un solo valor Int .
La función de update tiene solo una rama, que incrementa el Int , almacenado en el model .
La view representa el modelo y adjunta el evento DOM.
Vea cómo construir el ejemplo en Inicializar y compilar
import Html exposing (Html, button, text)
import Html exposing (beginnerProgram)
import Html.Events exposing (onClick)
main : Program Never
main =
beginnerProgram { model = 0, view = view, update = update }
-- UPDATE
type Msg
= Increment
update : Msg -> Int -> Int
update msg model =
case msg of
Increment ->
model + 1
-- VIEW
view : Int -> Html Msg
view model =
button [ onClick Increment ] [ text ("Increment: " ++ (toString model)) ]
Programa
program es una buena opción cuando su aplicación no requiere datos externos para la inicialización.
Es capaz de manejar suscripciones y comandos, lo que permite muchas más oportunidades para el manejo de E / S, como la comunicación HTTP o la interoperabilidad con JavaScript.
Se requiere que el estado inicial devuelva los Comandos de inicio junto con el Modelo.
La inicialización del program requerirá que se proporcionen subscriptions , junto con el model , la view y la update .
Ver la definición de tipo:
program :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program Never
Ejemplo
La forma más sencilla de ilustrar cómo puede usar las Suscripciones es configurar una comunicación Port simple con JavaScript.
Vea cómo compilar el ejemplo en Inicializar y compilar / incrustar en HTML
port module Main exposing (..)
import Html exposing (Html, text)
import Html exposing (program)
main : Program Never
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
port input : (Int -> msg) -> Sub msg
-- MODEL
type alias Model =
Int
init : ( Model, Cmd msg )
init =
( 0, Cmd.none )
-- UPDATE
type Msg = Incoming Int
update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
case msg of
Incoming x ->
( x, Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
input Incoming
-- VIEW
view : Model -> Html msg
view model =
text (toString model)
<!DOCTYPE html>
<html>
<head>
<script src='elm.js'></script>
</head>
<body>
<div id='app'></div>
<script>var app = Elm.Main.embed(document.getElementById('app'));</script>
<button onclick='app.ports.input.send(1);'>send</button>
</body>
</html>
Programa con banderas
programWithFlags tiene una sola diferencia del program .
Puede aceptar los datos en la inicialización de JavaScript:
var root = document.body;
var user = { id: 1, name: "Bob" };
var app = Elm.Main.embed( root, user );
Los datos, pasados de JavaScript se llaman Flags.
En este ejemplo, estamos pasando un objeto de JavaScript a Elm con información del usuario, es una buena práctica especificar un alias de tipo para las marcas.
type alias Flags =
{ id: Int
, name: String
}
Las banderas se pasan a la función init , produciendo el estado inicial:
init : Flags -> ( Model, Cmd Msg )
init flags =
let
{ id, name } =
flags
in
( Model id name, Cmd.none )
Es posible que note la diferencia con su tipo de firma:
programWithFlags :
{ init : flags -> ( model, Cmd msg ) -- init now accepts flags
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program flags
El código de inicialización es casi el mismo, ya que solo la función init es diferente.
main =
programWithFlags
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
Comunicación unidireccional entre padres e hijos.
El ejemplo muestra la composición de componentes y el mensaje de una sola vía pasando de padres a hijos.
La composición de componentes se basa en el etiquetado de mensajes con Html.App.map
En 0.18.0 HTML.App fue contraído en HTML
La composición de componentes se basa en el etiquetado de mensajes con Html.map
Ejemplo
Vea cómo construir el ejemplo en Inicializar y construir
module Main exposing (..)
import Html exposing (text, div, button, Html)
import Html.Events exposing (onClick)
import Html.App exposing (beginnerProgram)
main =
beginnerProgram
{ view = view
, model = init
, update = update
}
{- In v0.18.0 HTML.App was collapsed into HTML
Use Html.map instead of Html.App.map
-}
view : Model -> Html Msg
view model =
div []
[ Html.App.map FirstCounterMsg (counterView model.firstCounter)
, Html.App.map SecondCounterMsg (counterView model.secondCounter)
, button [ onClick ResetAll ] [ text "Reset counters" ]
]
type alias Model =
{ firstCounter : CounterModel
, secondCounter : CounterModel
}
init : Model
init =
{ firstCounter = 0
, secondCounter = 0
}
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
update : Msg -> Model -> Model
update msg model =
case msg of
FirstCounterMsg childMsg ->
{ model | firstCounter = counterUpdate childMsg model.firstCounter }
SecondCounterMsg childMsg ->
{ model | secondCounter = counterUpdate childMsg model.secondCounter }
ResetAll ->
{ model
| firstCounter = counterUpdate Reset model.firstCounter
, secondCounter = counterUpdate Reset model.secondCounter
}
type alias CounterModel =
Int
counterView : CounterModel -> Html CounterMsg
counterView model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, text (toString model)
, button [ onClick Increment ] [ text "+" ]
]
type CounterMsg
= Increment
| Decrement
| Reset
counterUpdate : CounterMsg -> CounterModel -> CounterModel
counterUpdate msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
Reset ->
0
Etiquetado de mensajes con Html.App.map
Los componentes definen sus propios mensajes, enviados después de los eventos DOM emitidos, por ejemplo. CounterMsg de comunicación padre-hijo
type CounterMsg
= Increment
| Decrement
| Reset
La vista de este componente enviará mensajes de tipo CounterMsg , por lo tanto, la firma del tipo de vista es Html CounterMsg .
Para poder reutilizar counterView vista interior del componente principal, tenemos que pasar todos los CounterMsg mensaje a través de los padres Msg .
Esta técnica se llama etiquetado de mensajes .
El componente principal debe definir mensajes para pasar mensajes secundarios:
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
FirstCounterMsg Increment es un mensaje etiquetado.
Para obtener un counterView para enviar mensajes etiquetados, debemos usar la función Html.App.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
El paquete HTML.App se HTML.App en el paquete HTML en v0.18.0
Para obtener un counterView para enviar mensajes etiquetados, debemos usar la función Html.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
Eso cambia el tipo de firma Html CounterMsg -> Html Msg por lo que es posible usar el contador dentro de la vista principal y manejar las actualizaciones de estado con la función de actualización de los padres.