iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 25
0
Modern Web

學會Elm寫前端系列 第 25

25 elm Q&A: 如何submit一個form?登入登出?

會想寫這一篇是因為其實官網的tutorial或是現在買得到的書,網站裡面,很少提到如何在elm裡做http post
這個動作。而相關的文章、套件也相當少。不只是這個,譬如要上傳檔案或是animation。甚至要讀取localStorage等等都還是缺乏相關套件,還是要使用外部的javascript去port進來。所以找了一些資料,自己拼拼湊湊出一個樣子。雖然 Http 套件裡有文檔說明,但沒有完整的使用。

submit form

ellie 上的範例檔。這是在 stackoverflow上面,有人問到如何submitform,無論是submit form,或是login/logout/signin等等,都是做一個http request/response的http post的動作。elm裡的http 套件裡,也有一個 Http.post

post : String -> Body -> Decoder a -> Request a
import Http
import Json.Decode exposing (list, string)

postBooks : Http.Request (List String)
postBooks =
  Http.post "https://example.com/books" Http.emptyBody (list string)
postComment newComment =
    Http.send AddCommentHttp
        (Http.post "https://jsonplaceholder.typicode.com/posts"
            (encodeNewComment newComment)
            decodeComment
        )


encodeNewComment : NewComment -> Http.Body
encodeNewComment newComment =
    Http.jsonBody <|
        Json.Encode.object
            [ ( "title", Json.Encode.string newComment.title )
            , ( "body", Json.Encode.string newComment.body )
            , ( "userId", Json.Encode.int newComment.userId )
            ]

在這個例子我們可以清楚看到,我們 post到某個網站後,http body 是一個jsonBody:

jsonBody : Value -> Body

Put some JSON value in the body of your Request. This will automatically add the Content-Type: application/json header.

等得到訊息後(http response),我們就可以用decode來處理,處理完後,用 Http.send 把這個訊息寄出去給 elm runtimeupdate知道我們下一步要做什麼。

localStorage with js interop

如果是login/logout,你得到的response會有一個session,你要存在localStorage裡,所以要和外界的javascript互動,而且你每次send to server的 東西,都要有這個session。其實到這裡就是一些基本的http
response/request的常識了。如果看不懂,可以要回去惡補一下,因為Elm是前端的東西,如果和後端去溝通,elm裡沒有幫你處理,但有提供相關的套件,以下是localStorage如何互動的範例

...    
    var storedState = localStorage.getItem('model');
    var startingState = storedState ? JSON.parse(storedState) : null;
    var elmApp = Elm.Main.fullscreen(startingState);
    elmApp.ports.setStorage.subscribe(function(state) {
        localStorage.setItem('model', JSON.stringify(state));
    });
    elmApp.ports.removeStorage.subscribe(function() {
        localStorage.removeItem('model');
    });
...
-- Helper to update model and set localStorage with the updated model

setStorageHelper : Model -> ( Model, Cmd Msg )

setStorageHelper model = 

    ( model, setStorage model )
-- Messages

...

-- Ports

port setStorage : Model -> Cmd msg  

port removeStorage : Model -> Cmd msg

-- Update

update : Msg -> Model -> (Model, Cmd Msg)

update msg model =

    case msg of

        ...
        GetTokenSuccess newToken ->
            setStorageHelper { model | token = newToken, password = "", errorMsg = "" }
        ...
        FetchProtectedQuoteSuccess newPQuote ->
            setStorageHelper { model | protectedQuote = newPQuote }
        LogOut ->
            ( { model | username = "", password = "", protectedQuote = "", token = "", errorMsg = "" },

authentication

這兩個就是我說的用Http.post,不過因為是Elm 0.17的文章了。有些符號有改變,譬如 Json.Decode裡的 (:=) 就是我們之前說的 field 而裡面用的Task方法,在0.18也有改變;有些方式也可以用 Http.post 來取代。0.18也出來一年多了,但是官方版的tutorial 一直沒有把 Task的教學在放回來;或是如何post也沒有多做說明。像是之前提到navigation/routing,官方的roadmap也是寫到下一版0.19也會做改進。總之,因為elm目前還在發展,加上社群相對小,仍然有一些功能待開發(但也都可以找到替代方案。)之後幾篇再來談談和elm相似的前端處理框架。


上一篇
24 elm Q&A: 可以在說一次Json.Decode嗎?
下一篇
26 elm和他的朋友們
系列文
學會Elm寫前端30

尚未有邦友留言

立即登入留言