Elm Language
L'architecture des ormes
Recherche…
Introduction
La méthode recommandée pour structurer vos applications s'appelle «l'architecture Elm».
Le programme le plus simple est constitué d'un model enregistrement stockant toutes les données qui pourraient être mises à jour, un type d'union Msg qui définit les moyens de votre programme met à jour ces données, une fonction update à Msg view update qui prend le modèle et un Msg et retourne un nouveau modèle, et une fonction view qui prend un modèle et renvoie le code HTML que votre page affichera. Chaque fois qu'une fonction retourne un Msg , le moteur d'exécution Elm l'utilise pour mettre à jour la page.
Programme débutant
Html a beginnerProgram principalement à des fins d'apprentissage.
beginnerProgram n'est pas capable de gérer des abonnements ou des commandes en cours d'exécution.
Il est uniquement capable de gérer les entrées utilisateur des événements DOM.
Il ne nécessite qu'une view de rendre le model et une update à update fonction pour gérer les changements d'état.
Exemple
Considérons cet exemple minimal de beginnerProgram .
Le model dans cet exemple consiste en une seule valeur Int .
La fonction de update n'a qu'une seule branche, qui incrémente l' Int , stockée dans le model .
La view le modèle et attache un clic sur l’événement DOM.
Voir comment construire l'exemple dans Initialize et build
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)) ]
Programme
program est un bon choix, lorsque votre application ne nécessite aucune donnée externe pour l'initialisation.
Il est capable de gérer les abonnements et les commandes, ce qui offre beaucoup plus d'opportunités pour gérer les E / S, telles que la communication HTTP ou l'interopérabilité avec JavaScript.
L'état initial est requis pour renvoyer les commandes de démarrage avec le modèle.
L'initialisation du program nécessitera des subscriptions , ainsi que le model , la view et la update .
Voir la définition du type:
program :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program Never
Exemple
Le moyen le plus simple d'illustrer comment vous pouvez utiliser les abonnements consiste à configurer une simple communication de port avec JavaScript.
Voir comment créer l'exemple dans Initialize and build / Embedding dans 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>
Programme avec des drapeaux
programWithFlags n'a qu'une seule différence avec le program .
Il peut accepter les données lors de l'initialisation à partir de JavaScript:
var root = document.body;
var user = { id: 1, name: "Bob" };
var app = Elm.Main.embed( root, user );
Les données transmises depuis JavaScript s'appellent Flags.
Dans cet exemple, nous transmettons un objet JavaScript à Elm avec des informations utilisateur, il est conseillé de spécifier un alias de type pour les indicateurs.
type alias Flags =
{ id: Int
, name: String
}
Les indicateurs sont transmis à la fonction init , produisant l'état initial:
init : Flags -> ( Model, Cmd Msg )
init flags =
let
{ id, name } =
flags
in
( Model id name, Cmd.none )
Vous remarquerez peut-être la différence par rapport à la signature de type:
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
Le code d'initialisation est presque identique, car seule la fonction d' init est différente.
main =
programWithFlags
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
Communication parent-enfant à sens unique
L'exemple montre la composition du composant et le message unidirectionnel passant du parent aux enfants.
La composition des composants repose sur le balisage des messages avec Html.App.map
En 0.18.0 HTML.App été réduit en HTML
La composition du composant repose sur le balisage des messages avec Html.map
Exemple
Voir comment construire l'exemple dans Initialiser et construire
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
Balisage de message avec Html.App.map
Les composants définissent leurs propres messages, envoyés après les événements DOM émis, par exemple. CounterMsg de la communication parent-enfant
type CounterMsg
= Increment
| Decrement
| Reset
La vue de ce composant enverra des messages de type CounterMsg , par conséquent la signature de type de vue est Html CounterMsg .
Pour pouvoir réutiliser counterView dans la vue du composant parent, nous devons transmettre chaque message CounterMsg via Msg du parent.
Cette technique s'appelle le marquage de message .
Le composant parent doit définir des messages pour la transmission des messages enfants:
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
FirstCounterMsg Increment est un message balisé.
Pour obtenir un counterView pour envoyer des messages marqués, nous devons utiliser la fonction Html.App.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
Le package HTML.App été réduit dans le package HTML en v0.18.0
Pour qu'un counterView envoie des messages marqués, nous devons utiliser la fonction Html.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
Cela modifie le type signature Html CounterMsg -> Html Msg , il est donc possible d'utiliser le compteur dans la vue parent et de gérer les mises à jour d'état avec la fonction de mise à jour du parent.