Spraw, aby Twój interfejs internetowy był niezawodny dzięki Elm
Opublikowany: 2022-03-11Ile razy próbowałeś debugować swój interfejs internetowy i zdarzyło Ci się zaplątać w kod radzenia sobie ze złożonymi łańcuchami zdarzeń?
Czy kiedykolwiek próbowałeś refaktoryzować kod dla interfejsu użytkownika, który ma do czynienia z wieloma komponentami zbudowanymi z jQuery, Backbone.js lub innymi popularnymi frameworkami JavaScript?
Jedną z najbardziej bolesnych rzeczy związanych z tymi scenariuszami jest próba śledzenia wielu nieokreślonych sekwencji zdarzeń oraz przewidywanie i naprawianie wszystkich tych zachowań. Po prostu koszmar!
Zawsze szukałem sposobów na ucieczkę od tego piekielnego aspektu webowego tworzenia front-endu. Backbone.js sprawdził się pod tym względem dla mnie, nadając webowym front-endom strukturę, której brakowało im od dłuższego czasu. Ale biorąc pod uwagę gadatliwość, jakiej wymaga robienie niektórych z najbardziej trywialnych rzeczy, nie okazało się to dużo lepsze.
Wtedy spotkałem Elma.
Elm to statycznie typowany język funkcjonalny oparty na języku programowania Haskell, ale z prostszą specyfikacją. Kompilator (również zbudowany przy użyciu Haskella) analizuje kod Elm i kompiluje go do JavaScript.
Elm został pierwotnie zbudowany do programowania front-end, ale inżynierowie oprogramowania znaleźli sposoby na wykorzystanie go również do programowania po stronie serwera.
Ten artykuł zawiera przegląd tego, w jaki sposób Elm może zmienić sposób, w jaki myślimy o tworzeniu frontonu internetowego, oraz wprowadzenie do podstaw tego funkcjonalnego języka programowania. W tym samouczku opracujemy prostą aplikację podobną do koszyka na zakupy, używając Elm.
Dlaczego Wiąz?
Elm obiecuje wiele zalet, z których większość jest niezwykle przydatna w osiągnięciu czystej architektury webowego front-endu. Elm oferuje lepszą wydajność renderowania HTML w porównaniu z innymi popularnymi frameworkami (nawet React.js). Co więcej, Elm pozwala programistom na pisanie kodu, który w praktyce nie generuje większości wyjątków wykonawczych, które nękają języki dynamicznie typowane, takie jak JavaScript.
Kompilator automatycznie wnioskuje typy i emituje przyjazne błędy, dzięki czemu programista jest świadomy każdego potencjalnego problemu przed uruchomieniem.
NoRedInk ma 36 000 linii Elm i po ponad roku produkcji nadal nie wytworzył ani jednego wyjątku w czasie wykonywania. [Źródło]
Nie musisz konwertować całej istniejącej aplikacji JavaScript tylko po to, aby wypróbować Elm. Dzięki doskonałej współpracy z JavaScriptem, możesz nawet wziąć tylko niewielką część istniejącej aplikacji i przepisać ją w Elm.
Elm ma również doskonałą dokumentację, która nie tylko daje dokładny opis tego, co ma do zaoferowania, ale także daje odpowiedni przewodnik po tworzeniu interfejsu internetowego zgodnie z architekturą Elm - coś, co jest świetne do modułowości, ponownego wykorzystania kodu i testowania .
Zróbmy prosty koszyk
Zacznijmy od bardzo krótkiego fragmentu kodu Elm:
import List exposing (..) cart = [] item product quantity = { product = product, qty = quantity } product name price = { name = name, price = price } add cart product = if isEmpty (filter (\item -> item.product == product) cart) then append cart [item product 1] else cart subtotal cart = -- we want to calculate cart subtotal sum (map (\item -> item.product.price * toFloat item.qty) cart)Każdy tekst poprzedzony
--jest komentarzem w Elm.
Tutaj definiujemy koszyk jako listę towarów, gdzie każda pozycja jest rekordem z dwiema wartościami (produkt, któremu odpowiada i ilość). Każdy produkt to rekord z nazwą i ceną.
Dodanie produktu do koszyka polega na sprawdzeniu, czy dany produkt już istnieje w koszyku.
Jeśli tak, nie robimy nic; w przeciwnym razie dodamy produkt do koszyka jako nowy przedmiot. Sprawdzamy, czy produkt już istnieje w koszyku, filtrując listę, dopasowując każdą pozycję do produktu i sprawdzając, czy otrzymana przefiltrowana lista jest pusta.
Aby obliczyć sumę częściową, iterujemy pozycje w koszyku, znajdując odpowiednią ilość produktu i cenę oraz sumując to wszystko.
To jest tak minimalistyczne, jak może uzyskać wózek i związane z nim funkcje. Zaczniemy od tego kodu i będziemy go ulepszać krok po kroku, czyniąc z niego kompletny komponent sieciowy lub program w terminologii Elma.
Zacznijmy od dodania typów do różnych identyfikatorów w naszym programie. Elm jest w stanie samodzielnie wywnioskować typy, ale aby jak najlepiej wykorzystać Elm i jego kompilator, zalecane jest jawne wskazywanie typów.
module Cart1 exposing ( Cart, Item, Product , add, subtotal , itemSubtotal ) -- This is module and its API definition {-| We build an easy shopping cart. @docs Cart, Item, Product, add, subtotal, itemSubtotal -} import List exposing (..) -- we need list manipulation functions {-| Cart is a list of items. -} type alias Cart = List Item {-| Item is a record of product and quantity. -} type alias Item = { product : Product, qty : Int } {-| Product is a record with name and price -} type alias Product = { name : String, price : Float } {-| We want to add stuff to a cart. This is a function definition, it takes a cart, a product to add and returns new cart -} add : Cart -> Product -> Cart {-| This is an implementation of the 'add' function. Just append product item to the cart if there is no such product in the cart listed. Do nothing if the product exists. -} add cart product = if isEmpty (filter (\item -> item.product == product) cart) then append cart [Item product 1] else cart {-| I need to calculate cart subtotal. The function takes a cart and returns float. -} subtotal : Cart -> Float {-| It's easy -- just sum subtotal of all items. -} subtotal cart = sum (map itemSubtotal cart) {-| Item subtotal takes item and return the subtotal float. -} itemSubtotal : Item -> Float {-| Subtotal is based on product's price and quantity. -} itemSubtotal item = item.product.price * toFloat item.qtyDzięki adnotacjom typu kompilator może teraz wykrywać problemy, które w przeciwnym razie spowodowałyby wyjątki w czasie wykonywania.
Jednak Elm nie poprzestaje na tym. Architektura Elm prowadzi programistów przez prosty wzorzec strukturyzowania ich front-endów internetowych i robi to poprzez koncepcje, które większość programistów już zna:
- Model: Modele przechowują stan programu.
- Widok: Widok to wizualna reprezentacja stanu.
- Aktualizacja: Aktualizacja to sposób na zmianę stanu.
Jeśli myślisz o części kodu dotyczącej aktualizacji jako kontrolerze, to masz coś bardzo podobnego do starego dobrego paradygmatu Model-View-Controller (MVC).
Ponieważ Elm jest czysto funkcjonalnym językiem programowania, wszystkie dane są niezmienne, co oznacza, że nie można zmienić modelu. Zamiast tego możemy stworzyć nowy model na podstawie poprzedniego, co robimy poprzez funkcje aktualizacji.
Dlaczego to jest takie wspaniałe?
W przypadku danych niezmiennych funkcje nie mogą już powodować skutków ubocznych. Otwiera to świat możliwości, w tym debugger podróży w czasie Elma, który omówimy wkrótce.
Widoki są renderowane za każdym razem, gdy zmiana w modelu wymaga zmiany widoku i zawsze będziemy mieli ten sam wynik dla tych samych danych w modelu - w podobny sposób, w jaki czysta funkcja zawsze zwraca ten sam wynik dla te same argumenty wejściowe.
Zacznij od głównej funkcji
Przejdźmy dalej i zaimplementujmy widok HTML dla naszej aplikacji koszyka.
Jeśli znasz Reacta, z pewnością docenisz to: Elm ma pakiet tylko do definiowania elementów HTML. Pozwala to na zaimplementowanie widoku za pomocą Elm, bez konieczności polegania na zewnętrznych językach szablonów.
Opakowania dla elementów HTML są dostępne w pakiecie Html :
import Html exposing (Html, button, table, caption, thead, tbody, tfoot, tr, td, th, text, section, p, h1)Wszystkie programy Elm zaczynają się od wykonania głównej funkcji:
type alias Stock = List Product type alias Model = { cart : Cart, stock : Stock } main = Html.beginnerProgram { model = Model [] [ Product "Bicycle" 100.50 , Product "Rocket" 15.36 , Product "Biscuit" 21.15 ] , view = view , update = update }Tutaj główna funkcja inicjuje program Elm z niektórymi modelami, widokiem i funkcją aktualizacji. Zdefiniowaliśmy kilka rodzajów produktów i ich ceny. Dla uproszczenia zakładamy, że posiadamy nieograniczoną liczbę produktów.
Prosta funkcja aktualizacji
Funkcja aktualizacji to miejsce, w którym nasza aplikacja ożywa.
Pobiera wiadomość i aktualizuje stan na podstawie treści wiadomości. Definiujemy go jako funkcję, która przyjmuje dwa parametry (wiadomość i aktualny model) i zwraca nowy model:
type Msg = Add Product update : Msg -> Model -> Model update msg model = case msg of Add product -> { model | cart = add model.cart product } Na razie zajmujemy się pojedynczym przypadkiem, gdy komunikat to Add product , w którym wywołujemy metodę add na cart z product .
Funkcja aktualizacji będzie rosła wraz ze wzrostem złożoności programu Elm.
Implementacja funkcji widoku
Następnie definiujemy widok naszego koszyka.
Widok to funkcja, która tłumaczy model na jego reprezentację HTML. Nie jest to jednak tylko statyczny kod HTML. Generator HTML jest w stanie wysyłać komunikaty z powrotem do aplikacji w oparciu o różne interakcje i zdarzenia użytkownika.
view : Model -> Html Msg view model = section [style [("margin", "10px")]] [ stockView model.stock , cartView model.cart ] stockView : Stock -> Html Msg stockView stock = table [] [ caption [] [ h1 [] [ text "Stock" ] ] , thead [] [ tr [] [ th [align "left", width 100] [ text "Name" ] , th [align "right", width 100] [ text "Price" ] , th [width 100] [] ] ] , tbody [] (map stockProductView stock) ] stockProductView : Product -> Html Msg stockProductView product = tr [] [ td [] [ text product.name ] , td [align "right"] [ text ("\t$" ++ toString product.price) ] , td [] [ button [ onClick (Add product) ] [ text "Add to Cart" ] ] ] Pakiet Html udostępnia opakowania dla wszystkich powszechnie używanych elementów jako funkcje o znanych nazwach (np. section funkcji generuje element <section> ).
Funkcja style , część pakietu Html.Attributes , generuje obiekt, który można przekazać do funkcji section w celu ustawienia atrybutu stylu w wynikowym elemencie.
Lepiej jest podzielić widok na oddzielne funkcje, aby uzyskać lepszą użyteczność.
Aby wszystko było proste, umieściliśmy CSS i niektóre atrybuty układu bezpośrednio w naszym kodzie widoku. Istnieją jednak biblioteki, które usprawniają proces stylizowania elementów HTML z kodu Elm.
Zwróć uwagę na button na końcu fragmentu kodu i sposób, w jaki powiązaliśmy komunikat Add product ze zdarzeniem kliknięcia przycisku.
Elm zajmuje się generowaniem całego niezbędnego kodu do powiązania funkcji zwrotnej z rzeczywistym zdarzeniem oraz generowaniem i wywoływaniem funkcji aktualizacji z odpowiednimi parametrami.
Na koniec musimy zaimplementować ostatnią część naszego widoku:
cartView : Cart -> Html Msg cartView cart = if isEmpty cart then p [] [ text "Cart is empty" ] else table [] [ caption [] [ h1 [] [ text "Cart" ]] , thead [] [ tr [] [ th [ align "left", width 100 ] [ text "Name" ] , th [ align "right", width 100 ] [ text "Price" ] , th [ align "center", width 50 ] [ text "Qty" ] , th [ align "right", width 100 ] [ text "Subtotal" ] ] ] , tbody [] (map cartProductView cart) , tfoot [] [ tr [style [("font-weight", "bold")]] [ td [ align "right", colspan 4 ] [ text ("$" ++ toString (subtotal cart)) ] ] ] ] cartProductView : Item -> Html Msg cartProductView item = tr [] [ td [] [ text item.product.name ] , td [ align "right" ] [ text ("$" ++ toString item.product.price) ] , td [ align "center" ] [ text (toString item.qty) ] , td [ align "right" ] [ text ("$" ++ toString (itemSubtotal item)) ] ] Tutaj zdefiniowaliśmy drugą część naszego widoku, w której renderujemy zawartość naszego koszyka. Chociaż funkcja widoku nie emituje żadnego komunikatu, nadal musi mieć zwracany typ Html Msg , aby kwalifikować się jako widok.
Widok nie tylko wyświetla zawartość koszyka, ale także oblicza i renderuje sumę częściową na podstawie zawartości koszyka.
Pełny kod tego programu Elm można znaleźć tutaj.
Gdybyś miał teraz uruchomić program Elm, zobaczyłbyś coś takiego:
Jak to wszystko działa?
Nasz program zaczyna się od dość pustego stanu z funkcji main - pustego koszyka z kilkoma zakodowanymi na stałe produktami.
Każdorazowe kliknięcie przycisku „Dodaj do koszyka” powoduje wysłanie wiadomości do funkcji aktualizacji, która następnie odpowiednio aktualizuje koszyk i tworzy nowy model. Za każdym razem, gdy model jest aktualizowany, funkcje widoku są wywoływane przez Elm w celu ponownego wygenerowania drzewa HTML.

Ponieważ Elm wykorzystuje podejście Virtual DOM, podobne do tego w React, zmiany w interfejsie użytkownika są wykonywane tylko wtedy, gdy jest to konieczne, zapewniając szybką wydajność.
Nie tylko sprawdzanie typu
Elm jest typowany statycznie, ale kompilator może sprawdzić znacznie więcej niż tylko typy.
Zróbmy zmianę w naszym typie Msg i zobaczmy, jak zareaguje na to kompilator:
type Msg = Add Product | ChangeQty Product StringZdefiniowaliśmy inny rodzaj komunikatu - coś, co zmieniłoby ilość produktu w koszyku. Jednak próba ponownego uruchomienia programu bez obsługi tego komunikatu w funkcji aktualizacji spowoduje wyświetlenie następującego błędu:
W kierunku bardziej funkcjonalnego wózka
Zauważ, że w poprzedniej sekcji użyliśmy ciągu jako typu wartości ilości. Dzieje się tak, ponieważ wartość będzie pochodzić z elementu <input> , który będzie typu string.
Dodajmy nową funkcję changeQty do modułu Cart . Zawsze lepiej jest zachować implementację wewnątrz modułu, aby móc ją później zmienić w razie potrzeby bez zmiany interfejsu API modułu.
{-| Change quantity of the product in the cart. Look at the result of the function. It uses Result type. The Result type has two parameters: for bad and for good result. So the result will be Error "msg" or a Cart with updated product quantity. -} changeQty : Cart -> Product -> Int -> Result String Cart {-| If the quantity parameter is zero the product will be removed completely from the cart. If the quantity parameter is greater then zero the quantity of the product will be updated. Otherwise (qty < 0) the error will be returned. -} changeQty cart product qty = if qty == 0 then Ok (filter (\item -> item.product /= product) cart) else if qty > 0 then Result.Ok (map (\item -> if item.product == product then { item | qty = qty } else item) cart) else Result.Err ("Wrong negative quantity used: " ++ (toString qty)) Nie powinniśmy zakładać, jak funkcja będzie używana. Możemy być pewni, że parametr qty będzie zawierał Int , ale wartość może być dowolna. Dlatego sprawdzamy wartość i zgłaszamy błąd, gdy jest ona nieprawidłowa.
Odpowiednio aktualizujemy również naszą funkcję update :
update msg model = case msg of Add product -> { model | cart = add model.cart product } ChangeQty product str -> case toInt str of Ok qty -> case changeQty model.cart product qty of Ok cart -> { model | cart = cart } Err msg -> model -- do nothing, the wrong input Err msg -> model -- do nothing, the wrong quantity Konwertujemy parametr quantity ciągu z wiadomości na liczbę przed jego użyciem. Jeśli ciąg zawiera nieprawidłową liczbę, zgłaszamy to jako błąd.
Tutaj zachowujemy model niezmieniony, gdy wystąpi błąd. Alternatywnie możemy po prostu zaktualizować model w taki sposób, aby zgłosić błąd jako komunikat w widoku, który użytkownik zobaczy:
type alias Model = { cart : Cart, stock : Stock, error : Maybe String } main = Html.beginnerProgram { model = Model [] -- empty cart [ Product "Bicycle" 100.50 -- stock , Product "Rocket" 15.36 , Product "Bisquit" 21.15 ] Nothing -- error (no error at beginning) , view = view , update = update } update msg model = case msg of Add product -> { model | cart = add model.cart product } ChangeQty product str -> case toInt str of Ok qty -> case changeQty model.cart product qty of Ok cart -> { model | cart = cart, error = Nothing } Err msg -> { model | error = Just msg } Err msg -> { model | error = Just msg } Jako atrybut błędu w naszym modelu używamy typu Maybe String . Może to inny typ, który może zawierać Nothing lub wartość określonego typu.
Po zaktualizowaniu funkcji widoku w następujący sposób:
view model = section [style [("margin", "10px")]] [ stockView model.stock , cartView model.cart , errorView model.error ] errorView : Maybe String -> Html Msg errorView error = case error of Just msg -> p [style [("color", "red")]] [ text msg ] Nothing -> p [] []Powinieneś to zobaczyć:
Próba wprowadzenia wartości nienumerycznej (np. „1a”) spowoduje wyświetlenie komunikatu o błędzie, jak pokazano na powyższym zrzucie ekranu.
Świat pakietów
Elm posiada własne repozytorium pakietów open source. Dzięki menedżerowi pakietów dla Elm wykorzystanie tej puli pakietów staje się dziecinnie proste. Chociaż wielkość repozytorium nie jest porównywalna z niektórymi innymi dojrzałymi językami programowania, takimi jak Python czy PHP, społeczność Elm ciężko pracuje nad wdrażaniem większej liczby pakietów każdego dnia.
Zauważ, że miejsca po przecinku w renderowanych cenach są naszym zdaniem niespójne?
Zastąpmy nasze naiwne użycie toString czymś lepszym z repozytorium: numeral-elm.
cartProductView item = tr [] [ td [] [ text item.product.name ] , td [ align "right" ] [ text (formatPrice item.product.price) ] , td [ align "center" ] [ input [ value (toString item.qty) , onInput (ChangeQty item.product) , size 3 --, type' "number" ] [] ] , td [ align "right" ] [ text (formatPrice (itemSubtotal item)) ] ] formatPrice : Float -> String formatPrice price = format "$0,0.00" price Używamy tutaj funkcji format z pakietu Numeral. Spowoduje to sformatowanie liczb w sposób, w jaki zwykle formatujemy waluty:
100.5 -> $100.50 15.36 -> $15.36 21.15 -> $21.15Automatyczne generowanie dokumentacji
Podczas publikowania pakietu do repozytorium Elm dokumentacja jest generowana automatycznie na podstawie komentarzy w kodzie. Możesz to zobaczyć w akcji, sprawdzając dokumentację naszego modułu Koszyk tutaj. Wszystkie te zostały wygenerowane z komentarzy widocznych w tym pliku: Cart.elm.
Prawdziwy debugger dla front-endu
Najbardziej oczywiste problemy są wykrywane i zgłaszane przez sam kompilator. Jednak żadna aplikacja nie jest zabezpieczona przed błędami logicznymi.
Ponieważ wszystkie dane w Elm są niezmienne i wszystko dzieje się za pośrednictwem komunikatów przekazywanych do funkcji aktualizacji, cały przepływ programu Elm można przedstawić jako serię zmian modelu. Dla debuggera Elm jest jak turowa gra strategiczna. Dzięki temu debugger może dokonywać naprawdę niesamowitych wyczynów, takich jak podróżowanie w czasie. Może poruszać się tam i z powrotem w przebiegu programu, przeskakując między różnymi zmianami modelu, które miały miejsce w czasie życia programu.
Możesz dowiedzieć się więcej o debugerze tutaj.
Interakcja z zapleczem
Mówisz, że zbudowaliśmy fajną zabawkę, ale czy Elm można wykorzystać do czegoś poważnego? Absolutnie.
Połączmy nasz interfejs koszyka z jakimś asynchronicznym zapleczem. Aby było ciekawie, zrealizujemy coś wyjątkowego. Załóżmy, że chcemy sprawdzić wszystkie koszyki i ich zawartość w czasie rzeczywistym. W prawdziwym życiu moglibyśmy użyć tego podejścia, aby wprowadzić dodatkowe możliwości marketingowe/sprzedażowe do naszego sklepu internetowego lub rynku, lub przedstawić sugestie użytkownikowi lub oszacować wymagane zasoby magazynowe i wiele więcej.
Dlatego przechowujemy koszyk po stronie klienta, a także informujemy serwer o każdym koszyku w czasie rzeczywistym.
Aby wszystko było proste, zaimplementujemy nasz back-end za pomocą Pythona. Pełny kod zaplecza można znaleźć tutaj.
Jest to prosty serwer sieciowy, który używa protokołu WebSocket i śledzi zawartość koszyka w pamięci. Aby uprościć sprawę, wyrenderujemy koszyk wszystkich innych osób na tej samej stronie. Można to łatwo zaimplementować na osobnej stronie lub nawet jako osobny program Elm. Na razie każdy użytkownik będzie mógł zobaczyć podsumowanie koszyków innych użytkowników.
Po uruchomieniu zaplecza będziemy musieli zaktualizować naszą aplikację Elm, aby wysyłać i odbierać aktualizacje koszyka na serwer. Użyjemy JSON do zakodowania naszych ładunków, dla których Elm ma doskonałe wsparcie.
CartEncoder.elm
Zaimplementujemy koder, aby przekonwertować nasz model danych Elm na reprezentację ciągu JSON. W tym celu musimy skorzystać z biblioteki Json.Encode.
module CartEncoder exposing (cart) import Cart2 exposing (Cart, Item, Product) import List exposing (map) import Json.Encode exposing (..) product : Product -> Value product product = object [ ("name", string product.name) , ("price", float product.price) ] item : Item -> Value item item = object [ ("product", product item.product) , ("qty", int item.qty) ] cart : Cart -> Value cart cart = list (map item cart) Biblioteka udostępnia pewne funkcje (takie jak string , int , float , object , itp.), które pobierają obiekty Elm i przekształcają je w ciągi zakodowane w formacie JSON.
CartDecoder.elm
Implementacja dekodera jest nieco trudniejsza, ponieważ wszystkie dane Elm mają typy i musimy zdefiniować, jaka wartość JSON musi zostać przekonwertowana na jaki typ:
module CartDecoder exposing (cart) import Cart2 exposing (Cart, Item, Product) -- decoding for Cart import Json.Decode exposing (..) -- will decode cart from string cart : Decoder (Cart) cart = list item -- decoder for cart is a list of items item : Decoder (Item) item = object2 Item -- decoder for item is an object with two properties: ("product" := product) -- 1) "product" of product ("qty" := int) -- 2) "qty" of int product : Decoder (Product) product = object2 Product -- decoder for product also an object with two properties: ("name" := string) -- 1) "name" ("price" := float) -- 2) "price"Zaktualizowana aplikacja Elm
Ponieważ ostateczny kod Elm jest nieco dłuższy, możesz go znaleźć tutaj. Oto podsumowanie zmian, które zostały wprowadzone w aplikacji front-endowej:
Opakowaliśmy naszą oryginalną funkcję update funkcją, która wysyła zmiany w zawartości koszyka na zaplecze za każdym razem, gdy koszyk jest aktualizowany:
updateOnServer msg model = let (newModel, have_to_send) = update msg model in case have_to_send of True -> -- send updated cart to server (!) newModel [ WebSocket.send server (encode 0 (CartEncoder.cart newModel.cart)) ] False -> -- do nothing (newModel, Cmd.none) Dodaliśmy również dodatkowy typ wiadomości ConsumerCarts String , aby otrzymywać aktualizacje z serwera i odpowiednio aktualizować model lokalny.
Widok został zaktualizowany, aby wyświetlić podsumowanie koszyków innych osób za pomocą funkcji consumersCartsView .
Połączenie WebSocket zostało ustanowione w celu subskrybowania zaplecza w celu nasłuchiwania zmian w koszykach innych osób.
subscriptions : Model -> Sub Msg subscriptions model = WebSocket.listen server ConsumerCarts server = "ws://127.0.0.1:8765" Zaktualizowaliśmy również naszą główną funkcję. Używamy teraz Html.program z dodatkowymi parametrami init i subscriptions . init określa początkowy model programu, a subscription określa listę subskrypcji.
Subskrypcja to sposób, w jaki możemy powiedzieć Elmowi, aby nasłuchiwał zmian na określonych kanałach i przekazywał te wiadomości do funkcji update .
main = Html.program { init = init , view = view , update = updateOnServer , subscriptions = subscriptions } init = ( Model [] -- empty cart [ Product "Bicycle" 100.50 -- stock , Product "Rocket" 15.36 , Product "Bisquit" 21.15 ] Nothing -- error (no error at beginning) [] -- consumer carts list is empty , Cmd.none)Wreszcie zajęliśmy się sposobem, w jaki dekodujemy wiadomość ConsumerCarts, którą otrzymujemy z serwera. Gwarantuje to, że dane, które otrzymamy z zewnętrznego źródła, nie zepsują aplikacji.
ConsumerCarts message -> case decodeString (Json.Decode.list CartDecoder.cart) message of Ok carts -> ({ model | consumer_carts = carts }, False) Err msg -> ({ model | error = Just msg, consumer_carts = [] }, False)Utrzymuj swoje front-endy w porządku
Wiąz jest inny. Wymaga od programisty innego myślenia.
Każdy, kto wywodzi się ze sfery JavaScript i podobnych języków, będzie próbował nauczyć się sposobu robienia rzeczy przez Elma.
Ostatecznie jednak Elm oferuje coś, z czym inne frameworki – nawet te najbardziej popularne – często mają trudności. Mianowicie, zapewnia środki do tworzenia solidnych aplikacji front-endowych bez plątaniny w ogromny, szczegółowy kod.
Elm usuwa również wiele trudności, jakie stwarza JavaScript, łącząc inteligentny kompilator z potężnym debuggerem.
Elm jest tym, za czym od tak dawna tęsknili programiści front-end. Teraz, gdy już widziałeś to w akcji, weź to na przejażdżkę i czerp korzyści, budując swój następny projekt internetowy w Elm.
