🌳
Walking though the Elm woods
  • Introduction
  • Structure of the book
  • Frequently asked questions
    • How can different types share data?
    • How to break Dependency Cycles?
    • How to structure an Elm project?
    • How to turn a Msg into a Cmd Msg?
    • How to update nested Records?
    • What are comparable types?
    • Why are Booleans bad?
    • 🔜Future topics
  • Recipes
    • Writing a Single Page Application
      • Share state across pages
      • Debounced Validation
      • Reusable views
    • Making impossible states Impossible
      • Non empty lists using Zippers
      • Restrict records using Opaque Types
      • Write safer functions using Phantom Types
    • Designing Elm package APIs
      • Create upwards compatible APIs
    • 🔜Future topics
  • Frameworks and packages
    • elm/parser
    • mdgriffith/elm-ui
    • 🔜Future topics
Powered by GitBook
On this page
  • Question
  • Answer
  • Further reading

Was this helpful?

  1. Frequently asked questions

How to structure an Elm project?

PreviousHow to break Dependency Cycles?NextHow to turn a Msg into a Cmd Msg?

Last updated 5 years ago

Was this helpful?

The answer to this question can be found in the . This question will therefore be removed at some time in the future.

Do not overuse: Files are allowed to get quite big without needing to split them.

Todo.elm
type Task =
    Task
        { name : String
        , completed : Bool
        }

createTask : String -> Task

completeTask : Task -> Task

type Form a =
    Valid a
    | Invalid a
    | Partial a

validate : (Form a -> Form ()) -> Form a -> Form a

type alias TaskForm =
    { name : String
    , validated : Bool
    }

validateTaskForm : Form TaskForm -> Form ()

type User =
    User
        { name : String
        , lastOnline : Posix
        }

createUser : String -> Posix -> User

type alias UserForm =
    { name : String
    , password : String
    , validated : Bool
    }

validateUserForm : Form UserForm -> Form ()

type alias TodoPageModel =
    { form :
        Form TaskForm
    , todo : Array Task
    , User : User
    }

type alias LoginPageModel =
    Form UserForm

type Model =
    LoginPage LoginPageModel
    | TodoPage TodoPageModel

type LoginPageMsg =
    NameEntered String
    | PasswordEntered String
    | Submited 
    | GotTime Posix

type TodoPageMsg =
    LoggedOut
    | TaskAdded String
    | TaskCompleted Int
    | TaskDeleted

type Msg =
    LoginSpecific LoginPageMsg
    | TodoSpecific TodoPageMsg

init : () -> Model

updateTodoPage : TodoPageModel -> (TodoPageModel, Cmd TodoPageMsg)

updateLoginPage : LoginPageModel -> (LoginPageModel, Cmd LoginPageMsg)

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

viewUserForm : Form UserForm -> Html Msg

viewLoginPage : LoginPageModel -> Html Msg

viewTask : Task -> Html Msg

viewTaskForm : Form TaskForm -> Html Msg

viewTodoPage : TodoPageModel -> Html Msg

view : Model -> Html Msg

subscription : Model -> Sub Msgexpos
Main.elm
importing Todo.Page.Login as LoginPage
importing Todo.Page.Todo as TodoPage

type Model =
    LoginPage LoginPage.Model
    | TodoPage TodoPage.Model

type Msg =
    LoginSpecific LoginPage.Msg
    | TodoSpecific TodoPage.Msg

init : () -> Model

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

view : Model -> Html Msg

subscription : Model -> Sub Msg
Page/Login.elm
module Todo.Page.Login exposing (Model,Msg,view,update)

importing Todo.Data.Form as Form exposing (Form)
importing Todo.Data.User as User exposing (User,UserForm)

type alias Model =
    Form UserForm
    
type Msg =
    NameEntered String
    | PasswordEntered String
    | Submited 
    | GotTime Posix

update : Model -> (Model, Cmd Msg)

viewUserForm : Form UserForm -> Html Msg

view : Model -> Html Msg
Page/Todo.elm
module Todo.Page.Login exposing (Model,Msg,view,update)

importing Todo.Data.Task as Task exposing (Task,TaskForm)
importing Todo.Data.User as User exposing (User,UserForm)

type alias Model =
    { form :
        Form TaskForm
    , todo : Array Task
    , User : User
    }

type Msg =
    LoggedOut
    | TaskAdded String
    | TaskCompleted Int
    | TaskDeleted

update : Model -> (Model, Cmd Msg)

viewTask : Task -> Html Msg

viewTaskForm : Form TaskForm -> Html Msg

view : Model -> Html Msg
Data/Task.elm
module Todo.Data.Task exposing (Task,create,complete,validate)

importing Todo.Data.Form as Form exposing (Form)

type Task =

    Task
        { name : String
        , completed : Bool
        }

create : String -> Task

complete : Task -> Task

type alias TaskForm =
    { name : String
    , validated : Bool
    }

validate : Form TaskForm -> Form ()
Data/Form.elm
module Todo.Data.Form exposing (Form(..),validate)

type Form a =
    Valid a
    | Invalid a
    | Partial a

validate : (Form a -> Form ()) -> Form a -> Form a
Data/User.elm
module Todo.Data.User exposing (User,UserForm,create,validate)

importing Todo.Data.Form as Form exposing (Form)

type User =
    User
        { name : String
        , lastOnline : Posix
        }

create : String -> Posix -> User

type alias UserForm =
    { name : String
    , password : String
    , validated : Bool
    }

validate : Form UserForm -> Form ()

Question

How should I structure my Elm project?

Answer

Use the following file structure:

  • Main.elm - Contains the main code

  • Data.elm - Contains utility functions for types (like constants)

    • Data/.. - Contains types. Group them logically, for example User and UserForm into Data/User.elm.

  • View.elm - Contains utility functions for views (like view specific constants and very general function.)

    • View/.. - Contains different Views. Sometimes a type has different views. A login page might have a special view for a wrong login.

  • Page.elm - Contains utility functions for pages. For page-transitions its handy to store the different models in here.

    • Page/.. - Contains a Model/View/Update for every page.

  • .. - in the same style you can add your project specific folders like a separate folder for validation.

Further reading

📄Article:

👥Thread:

📄Article: Tour of an by Richard Feldman

🎥Video: by Evan Czaplicki

Beginning Elm book
NoRedInk Style Guide
Code structure/architecture
Open-Source Elm SPA
The life of a file