Skip to content

Instantly share code, notes, and snippets.

@mdemin914
Created January 24, 2017 03:18
Show Gist options
  • Save mdemin914/0baf793dce856ffe57ce31979ad600d6 to your computer and use it in GitHub Desktop.
Save mdemin914/0baf793dce856ffe57ce31979ad600d6 to your computer and use it in GitHub Desktop.
Composing pages with a nav
module Main exposing (..)
import Html exposing (..)
import Html.Events exposing (onInput, onClick)
-- MODELS
type alias Model =
{ page : Page
, page1model : Page1Model
, page2model : Page2Model
}
model : Model
model =
Model Page1 emptyPage1 emptyPage2
type alias Page1Model =
{ field1 : String
, field2 : String
}
emptyPage1 : Page1Model
emptyPage1 =
{ field1 = ""
, field2 = ""
}
type alias Page2Model =
{ field1 : String
, field2 : String
}
emptyPage2 : Page2Model
emptyPage2 =
{ field1 = ""
, field2 = ""
}
type Page
= Page1
| Page2
type Msg
= Msg1 Page1Msg
| Msg2 Page2Msg
| Navigate String
| Logout
type Page1Msg
= DoSomething1
| Navigate1 String
| Logout1
type Page2Msg
= DoSomething2
| Navigate2 String
| Logout2
-- UPDATE
page1update : Page1Msg -> Page1Model -> ( Page1Model, Cmd Msg )
page1update msg model =
case msg of
DoSomething1 ->
( { model | field1 = "1" }, Cmd.none )
Navigate1 url ->
( model, navigate url )
Logout1 ->
( model, logout )
page2update : Page2Msg -> Page2Model -> ( Page2Model, Cmd Msg )
page2update msg model =
case msg of
DoSomething2 ->
( { model | field2 = "2" }, Cmd.none )
Navigate2 url ->
( model, navigate url )
Logout2 ->
( model, logout )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Msg1 msg1 ->
let
( newModel, cmd ) =
page1update msg1 model.page1model
in
( { model | page1model = newModel }, cmd )
Msg2 msg2 ->
let
( newModel, cmd ) =
page2update msg2 model.page2model
in
( { model | page2model = newModel }, cmd )
Navigate url ->
( model, navigate url )
Logout ->
( model, logout )
-- VIEW
-- super complex page which does a bunch of stuff not related to page2
-- option 1: change the the functions to all return Html Msg and
-- map everything in the page
-- pros: dont need to add a logout / navigate msg for each page
-- cons: a lot of Html.maps?
page1view_ : Page1Model -> Html Msg
page1view_ model =
div []
[ nav
, Html.map Msg1 (div [ onClick DoSomething1 ] [ text model.field1 ])
, Html.map Msg1 (div [ onClick DoSomething1 ] [ text model.field2 ])
]
-- super complex page which does a bunch of stuff
-- option 2: implement Navigate and Logout as a Page1Msg / Page2Msg for each page
-- and implement nav to accept msg instead of Msg
-- pros: dont need to Html.Map everything
-- cons: duplication of Navigate/Logout methods for each page
page1view__ : Page1Model -> Html Page1Msg
page1view__ model =
div []
[ nav_ Navigate1 Logout1
, div [ onClick DoSomething1 ] [ text model.field1 ]
, div [ onClick DoSomething1 ] [ text model.field2 ]
]
-- option 3: ???????
-- what is the best practice here to have a nav that can be used across pages
option3 : Html msg
option3 =
div [] []
--nav stuff
nav : Html Msg
nav =
div []
[ a [ onClick (Navigate "Page1") ] [ text "Page 1" ]
, a [ onClick (Navigate "Page2") ] [ text "Page 2" ]
, a [ onClick Logout ] [ text "Logout" ]
]
nav_ : (String -> msg) -> msg -> Html msg
nav_ navigate logout =
div []
[ a [ onClick (navigate "Page1") ] [ text "Page 1" ]
, a [ onClick (navigate "Page2") ] [ text "Page 2" ]
, a [ onClick logout ] [ text "Logout" ]
]
-- super complex page with a bunch of stuff
-- and it also has a nav bar, the messages are duplicated
page2view : Page2Model -> Html Page2Msg
page2view model =
div [ onClick DoSomething2 ]
[ nav_ Navigate2 Logout2
, text model.field2
]
-- main function that shows everything when needed
view : Model -> Html Msg
view model =
div []
[ page1view_ model.page1model
, Html.map Msg2 (page2view model.page2model)
]
-- Commands
logout : Cmd msg
logout =
Cmd.none
navigate : String -> Cmd msg
navigate url =
Cmd.none
--
init : ( Model, Cmd Msg )
init =
( model
, Cmd.none
)
main : Program Never Model Msg
main =
Html.program
{ init = init
, update = update
, subscriptions = \_ -> Sub.none
, view = view
}
@pdamoc
Copy link

pdamoc commented Jan 24, 2017

Here is another approach:

module Main exposing (..)

import Html exposing (..)
import Html.Events exposing (onInput, onClick)


-- MODELS


type alias Model =
    { page : Page
    , page1model : Page1Model
    , page2model : Page2Model
    }


model : Model
model =
    Model Page1 emptyPage1 emptyPage2


type alias Page1Model =
    { field1 : String
    , field2 : String
    }


emptyPage1 : Page1Model
emptyPage1 =
    { field1 = ""
    , field2 = ""
    }


type alias Page2Model =
    { field1 : String
    , field2 : String
    }


emptyPage2 : Page2Model
emptyPage2 =
    { field1 = ""
    , field2 = ""
    }


type Page
    = Page1
    | Page2


type Msg
    = Msg1 Page1Msg
    | Msg2 Page2Msg
    | Navigate String
    | Logout


type Page1Msg
    = DoSomething1
    | Logout1


type Page2Msg
    = DoSomething2
    | Logout2



-- UPDATE


type alias Page1Cfg msg =
    { lift : Page1Msg -> msg
    , logout : Cmd msg
    }


page1update : Page1Cfg msg -> Page1Msg -> Page1Model -> ( Page1Model, Cmd msg )
page1update cfg msg model =
    case msg of
        DoSomething1 ->
            ( { model | field1 = "1" }, Cmd.map cfg.lift pageOneCmd )

        Logout1 ->
            ( model, cfg.logout )


type alias Page2Cfg msg =
    { lift : Page2Msg -> msg
    , logout : Cmd msg
    }


page2update : Page2Cfg msg -> Page2Msg -> Page2Model -> ( Page2Model, Cmd msg )
page2update cfg msg model =
    case msg of
        DoSomething2 ->
            ( { model | field2 = "2" }, Cmd.map cfg.lift pageTwoCmd )

        Logout2 ->
            ( model, cfg.logout )


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Msg1 msg1 ->
            let
                cfg =
                    { lift = Msg1, logout = logout }

                ( newModel, cmd ) =
                    page1update cfg msg1 model.page1model
            in
                ( { model | page1model = newModel }, cmd )

        Msg2 msg2 ->
            let
                cfg =
                    { lift = Msg2, logout = logout }

                ( newModel, cmd ) =
                    page2update cfg msg2 model.page2model
            in
                ( { model | page2model = newModel }, cmd )

        Navigate url ->
            ( model, navigate url )

        Logout ->
            ( model, logout )



-- VIEW
-- super complex page which does a bunch of stuff not related to page2
-- option 1: change the the functions to all return Html Msg and
-- map everything in the page
-- pros: dont need to add a logout / navigate msg for each page
-- cons: a lot of Html.maps?


type alias Page1ViewCfg msg =
    { lift : Page1Msg -> msg
    , nav : Html msg
    }


page1view : Page1ViewCfg msg -> Page1Model -> Html msg
page1view cfg model =
    div []
        [ cfg.nav
        , div [ onClick (cfg.lift <| DoSomething1) ] [ text model.field1 ]
        , div [ onClick (cfg.lift <| DoSomething1) ] [ text model.field2 ]
        ]



-- super complex page which does a bunch of stuff
-- option 2: implement Navigate and Logout as a Page1Msg / Page2Msg for each page
-- and implement nav to accept msg instead of Msg
-- pros: dont need to Html.Map everything
-- cons: duplication of Navigate/Logout methods for each page
-- option 3: ???????
-- what is the best practice here to have a nav that can be used across pages


option3 : Html msg
option3 =
    div [] []



--nav stuff


nav : Html Msg
nav =
    div []
        [ a [ onClick (Navigate "Page1") ] [ text "Page 1" ]
        , a [ onClick (Navigate "Page2") ] [ text "Page 2" ]
        , a [ onClick Logout ] [ text "Logout" ]
        ]



-- super complex page with a bunch of stuff
-- and it also has a nav bar, the messages are duplicated


type alias Page2ViewCfg msg =
    { lift : Page2Msg -> msg
    , nav : Html msg
    }


page2view : Page2ViewCfg msg -> Page2Model -> Html msg
page2view cfg model =
    div [ onClick (cfg.lift <| DoSomething2) ]
        [ cfg.nav
        , text model.field2
        ]



-- main function that shows everything when needed


view : Model -> Html Msg
view model =
    div []
        [ page1view { lift = Msg1, nav = nav } model.page1model
        , page2view { lift = Msg2, nav = nav } model.page2model
        ]



-- Commands


logout : Cmd msg
logout =
    Cmd.none


navigate : String -> Cmd msg
navigate url =
    Cmd.none


pageOneCmd : Cmd Page1Msg
pageOneCmd =
    Cmd.none


pageTwoCmd : Cmd Page2Msg
pageTwoCmd =
    Cmd.none



--


init : ( Model, Cmd Msg )
init =
    ( model
    , Cmd.none
    )


main : Program Never Model Msg
main =
    Html.program
        { init = init
        , update = update
        , subscriptions = \_ -> Sub.none
        , view = view
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment