Giter Site home page Giter Site logo

fun-web's Introduction

Steps

  1. Install Phoenix tasks for Mix

    mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
    
  2. Create new Phoenix application

    mix phoenix.new secounter --no-ecto
    
  3. Change directory to newly created project

    cd secounter
    
  4. Start Phoenix application and open browser at http://localhost:4000

    mix phoenix.server
    
  5. Swap content of web/templates/layout/app.html.eex to change look and feel of application

    Snippet: secapphtml

  6. Create directory for Elm source code

    mkdir web/elm
    
  7. Install elm-brunch as a development dependency

    npm install --save-dev elm-brunch
    
  8. Install Elm package to support WebSocket

    cd web/elm
    elm package install elm-lang/websocket
    
  9. Add watching of web/elm directory in brunch-config.js in section watched.

    watched: [
      "web/elm",
      "web/static",
      "test/static"
    ]
    

    Snippet: secbrwelm

  10. Add elmBrunch plugin in brunch-config.js in section plugins

    elmBrunch: {
      elmFolder: "web/elm",
      mainModules: ["App.elm"],
      outputFolder: "../static/vendor"
    },
    

    Snippet: secbrelm

  11. Swap content of web/templates/page/index.html.eex to be able to include Elm application inside container

    <div id="elm-container"></div>
    

    Snippet: secappcnt

  12. Attach application to be able to view it in file secounter/web/static/js/app.js

    ```
    // Set up Elm App
    const elmDiv = document.querySelector("#elm-container");
    const elmApp = Elm.SecounterApp.embed(elmDiv);
    
    ```
    Snippet: **secattcnt**
    
  13. Create file secounter/web/elm/App.elm with next initial content

    module SecounterApp exposing (..)
    
    import Html exposing (..)
    import Html.App as App
    import Html.Attributes exposing (..)
    import Html.Events exposing (..)
    import WebSocket
    import Json.Encode as Encode
    import Json.Decode exposing (..)
    import String
    
    
    main =
    App.beginnerProgram { model = 0, view = view, update = update }
    
    
    type MsgType
    	= Increment
        | Decrement
    
    
    update : MsgType -> number -> number
    update msg model =
    	case msg of
        	Increment ->
            	model + 1
    
            Decrement ->
    	        model - 1
    
    
    view : a -> Html MsgType
    view model =
    	div [ style [ ( "margin", "10px" ) ] ]
        	[ button [ onClick Decrement ] [ text "-" ]
            , strong [ style [ ( "margin", "10px" ) ] ] [ text (toString model) ]
    	    , button [ onClick Increment ] [ text "+" ]
        	, div [ style [ ( "font-weight", "bold" ), ( "padding-top", "10px" ) ] ] [ text "Debug:" ]
        ]
    
    

    Snippet: secelmapp

  14. Let's write some code to allow increment/decrement functionality over Phoenix channels starting from channel message type

    type alias ChannelMsg =
        { topic : String
        , event : String
        , payload : String
        , ref : String
        }
    
    

    Snippet: secelmappcnmsg

  15. Now let's add some utility code to handle message conversion and sending to channel

    sendChannelMsg : ChannelMsg -> Cmd a
    sendChannelMsg msg =
        WebSocket.send sockerUrl (encodeChannelMsg msg)
    
    
    prepareChannelMsg : String -> Int -> ChannelMsg
    prepareChannelMsg action counter =
        ChannelMsg "counter:lobby" action (toString counter) action
    
    
    encodeChannelMsg : ChannelMsg -> String
    encodeChannelMsg msg =
        Encode.object
            [ ( "topic", Encode.string msg.topic )
            , ( "event", Encode.string msg.event )
            , ( "payload", Encode.object [ ( "body", Encode.string msg.payload ) ] )
            , ( "ref", Encode.string msg.ref )
            ]
            |> Encode.encode 0
    
    
    decodeChannelMsg : Decoder ChannelMsg
    decodeChannelMsg =
        object4 ChannelMsg
            ("topic" := string)
            ("event" := string)
            ("payload" := oneOf [ at [ "body" ] string, succeed "" ])
            (oneOf [ "ref" := string, succeed "" ])
    
    
    sockerUrl : String
    sockerUrl =
        "ws://localhost:4000/socket/websocket"
    

    Snippet: secelmapputils

  16. Let's create Phoenix channel handler

    mix phoenix.gen.channel Counter
    
  17. Register created channel in secounter/web/channels/user_socket.ex file

    channel "counter:*", Secounter.CounterChannel
    

    Snippet: secexregcn

  18. Now let's join Phoenix channel from Elm

    type MsgType
        = Increment
    	| Decrement
    	| Join
    

    Snippet: secelmappjntp

  19. Now let's fix compilation error related to add new MsgType Join by redefining update function

update : MsgType -> Model -> ( Model, Cmd MsgType ) update msg { counter, message } = case msg of Join -> ( Model counter message , sendChannelMsg (ChannelMsg "counter:lobby" "phx_join" "rooms:lobby" "ui") )

    Increment ->
        ( Model counter message
        , sendChannelMsg (prepareChannelMsg "increment" counter)
        )

    Decrement ->
        ( Model counter message
        , sendChannelMsg (prepareChannelMsg "decrement" counter)
        )
```
Snippet: **secelmappupjn**
  1. Now let's define our model to handle application state

    type alias Model =
    	{ counter : Int
        , message : String
    	}
    

    Snippet: secelmappmodel

  2. Now let's redefine main function

    main =
    App.program
        { init = init Join
        , view = view
        , update = update
        , subscriptions = subscriptions
        }
    

    Snippet: secelmappupmn

  3. Now let's define init function and subscriptions, during init we're going to join Phoenix channel and subscriptions will receive messages from channel

    init : MsgType -> ( Model, Cmd MsgType )
    init action =
    	(update action (Model 0 ""))
    
    subscriptions : Model -> Sub MsgType
    subscriptions model =
    	WebSocket.listen sockerUrl Receive
    

    Snippet: secelmappintsub

  4. Now let's add new MsgType to handle messages from channel by updating our application UI

    type MsgType
    	= Increment
        | Decrement
    	| Join
        | Receive String
    

    Snippet: secelmappaddrcv

  5. Now we need to align out update function by adding code to handle Receive MsgType. Now after refresh we can see a response from Phoenix on successful established connection to channel

    Receive msgFromChannel ->
            case decodeString decodeChannelMsg msgFromChannel of
                Err msg ->
                    ( Model counter (msg ++ msgFromChannel), Cmd.none )
    
                Ok value ->
                    ( Model (Result.withDefault counter (String.toInt value.payload)) msgFromChannel, Cmd.none )    	
    

    Snippet: secelmappaddrcvhdnl

  6. Now let's add some Elixir code to handle incoming messages over channels in secounter/web/channels/counter_channel.ex

      def handle_in("increment", payload, socket) do
        broadcast! socket, "increment", %{"body" => "#{String.to_integer(payload["body"]) + 1}"}
    	{:noreply, socket}
      end
    
      def handle_in("decrement", payload, socket) do
    	broadcast! socket, "decrement", %{"body" => "#{String.to_integer(payload["body"]) - 1}"}
        {:noreply, socket}
      end
    

    Snippet: secexaddhndlrs

  7. Now let's fix our view function to interpret changes happened to model. After that UI looks ok, however we did not align our changes at backend to handle increment/decrement

    view : Model -> Html MsgType
    view model =
    	div [ style [ ( "padding", "10px" ) ] ]
        	[ button [ onClick Decrement ] [ text "-" ]
            , strong [ style [ ( "padding", "10px" ) ] ] [ text (toString model.counter) ]
    	    , button [ onClick Increment ] [ text "+" ]
        	, div [ style [ ( "font-weight", "bold" ), ( "padding", "10px" ) ] ] [ text "Debug:" ]
            , div [] [ text model.message ]
        	]
    

    Snippet: secelmappupview

fun-web's People

Contributors

webdizz avatar

Watchers

 avatar James Cloos avatar  avatar

Forkers

twjohnson

fun-web's Issues

Small updates needed for elm 0.18

Looks like some things changed in elm 0.18 ...

Step 13 and 21:
Replacing App with Html seemed to work for elm 0.18

main =
Html.beginnerProgram {

and

main =
Html.program

Step 15:
Replacing object4 with map4 and using field seemed to work.

decodeChannelMsg : Decoder ChannelMsg
decodeChannelMsg =
map4 ChannelMsg
(field "topic" string)
(field "event" string)
(field "payload" (oneOf [ field "body" string, succeed "" ]))
(field "ref" (oneOf [ string, succeed "" ]))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.