Elm Language
Architektura wiązów
Szukaj…
Wprowadzenie
Zalecany sposób strukturyzacji aplikacji to „Architektura wiązu”.
Najprostszy program składa się z rekordu model przechowującego wszystkie dane, które mogą zostać zaktualizowane, typu unii Msg który definiuje sposób, w jaki program aktualizuje te dane, update funkcji, która pobiera model i Msg i zwraca nowy model oraz view funkcji, który bierze model i zwraca HTML, który wyświetli twoja strona. Za każdym razem, gdy funkcja zwraca Msg , środowisko wykonawcze Elm używa jej do aktualizacji strony.
Program dla początkujących
HTML ma program dla beginnerProgram głównie do celów edukacyjnych.
beginnerProgram nie obsługuje subskrypcji ani nie uruchamia poleceń.
Jest w stanie obsłużyć tylko dane wejściowe użytkownika ze zdarzeń DOM.
Wymaga jedynie view do renderowania model i funkcji update do obsługi zmian stanu.
Przykład
Rozważ ten minimalny przykład programu dla beginnerProgram .
model w tym przykładzie składa się z pojedynczej wartości Int .
Funkcja update ma tylko jedną gałąź, która zwiększa Int , przechowywaną w model .
view renderuje model i dołącza zdarzenie DOM.
Zobacz, jak zbudować przykład w Inicjalizuj i buduj
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)) ]
Program
program jest dobrym wyborem, gdy aplikacja nie wymaga żadnych danych zewnętrznych do inicjalizacji.
Jest w stanie obsługiwać subskrypcje i polecenia, co zapewnia znacznie więcej możliwości obsługi operacji we / wy, takich jak komunikacja HTTP lub interakcja z JavaScript.
Stan początkowy jest wymagany do zwrócenia poleceń startowych wraz z modelem.
Inicjalizacja program będzie wymagała dostarczenia subscriptions wraz z model , view i update .
Zobacz definicję typu:
program :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
}
-> Program Never
Przykład
Najprostszym sposobem zilustrowania sposobu korzystania z subskrypcji jest skonfigurowanie prostej komunikacji portu za pomocą JavaScript.
Zobacz, jak zbudować przykład w Inicjalizuj i wbuduj / Osadź w 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>
Program z flagami
programWithFlags ma tylko jedną różnicę od program .
Może zaakceptować dane po inicjalizacji z JavaScript:
var root = document.body;
var user = { id: 1, name: "Bob" };
var app = Elm.Main.embed( root, user );
Dane przekazane z JavaScript nazywa się Flagi.
W tym przykładzie przekazujemy obiekt JavaScript do Elm z informacjami o użytkowniku, dobrą praktyką jest określenie Aliasu typu dla flag.
type alias Flags =
{ id: Int
, name: String
}
Flagi są przekazywane do funkcji init , tworząc stan początkowy:
init : Flags -> ( Model, Cmd Msg )
init flags =
let
{ id, name } =
flags
in
( Model id name, Cmd.none )
Możesz zauważyć różnicę w stosunku do podpisu:
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
Kod inicjalizacji wygląda prawie tak samo, ponieważ różni się tylko funkcją init .
main =
programWithFlags
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
Jednokierunkowa komunikacja rodzic-dziecko
Przykład pokazuje skład komponentu i jednokierunkowe przekazywanie wiadomości od rodzica do dziecka.
Kompozycja składników opiera się na znakowaniu wiadomości za pomocą Html.App.map
W wersji 0.18.0 HTML.App został zwinięty w HTML
Kompozycja składników opiera się na znakowaniu wiadomości za pomocą Html.map
Przykład
Zobacz, jak zbudować przykład w Inicjalizuj i buduj
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
Oznaczanie wiadomości za pomocą Html.App.map
Komponenty definiują własne wiadomości wysyłane po wysłanych zdarzeniach DOM, np. CounterMsg z komunikacji rodzic-dziecko
type CounterMsg
= Increment
| Decrement
| Reset
Widok tego komponentu wyśle wiadomości typu CounterMsg , dlatego sygnatura typu widoku to Html CounterMsg .
Aby móc ponownie użyć counterView w widoku komponentu nadrzędnego, musimy przekazać każdą wiadomość CounterMsg przez Msg rodzica.
Ta technika nazywa się tagowaniem wiadomości .
Komponent nadrzędny musi zdefiniować komunikaty do przekazywania wiadomości potomnych:
type Msg
= FirstCounterMsg CounterMsg
| SecondCounterMsg CounterMsg
| ResetAll
FirstCounterMsg Increment jest oznaczoną wiadomością.
Aby uzyskać counterView do wysyłania oznaczonych wiadomości, musimy użyć funkcji Html.App.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
Pakiet HTML.App został zwinięty w pakiecie HTML w wersji v0.18.0
Aby uzyskać counterView do wysyłania oznaczonych wiadomości, musimy użyć funkcji Html.map :
Html.map FirstCounterMsg (counterView model.firstCounter)
To zmienia podpis typu Html CounterMsg -> Html Msg dzięki czemu możliwe jest użycie licznika w widoku nadrzędnym i obsługa aktualizacji stanu za pomocą funkcji aktualizacji rodzica.