Webbhuset.Internal.PID.PID
A PID is an identifier for a Process.
A component is a independent part in the system and has a single responsibility. They can not import other components.
Components are like normal elm programs. They have their own model, they can do commands and subscriptions. In addition to that they also have out messages.
This means you will have two Msg
types: MsgIn
and MsgOut
. MsgIn
is conceptually the same as your normal Msg
type would be.
MsgOut is a way to "tell" the rest of the system that something happened in your component.
For Example, the msg types for a login form component could look like this:
type MsgIn
= EmailFieldChanged String
| PasswordFieldChanged String
| SubmitbuttonClicked
type MsgOut
= FormWasSubmitted
{ email : String
, password : String
}
From the system's perspective, this is all you need to care about. This is the public API for the component.
The normal output tuple from the init
and update
functions are replaced with
a 3-Tuple:
( Model, List MsgOut, Cmd MsgIn )
A UI-component is very similar in concept to a Browser.element program.
{ init : PID -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, update : msgIn -> model -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, view : model -> Html msgIn
, onSystem : SystemEvent -> SystemEvent.Handling msgIn
, subs : model -> Platform.Sub.Sub msgIn
}
UI Component Type
The service component does not have any view function. Remember Platform.worker
?
{ init : PID -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, update : msgIn -> model -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, onSystem : SystemEvent -> SystemEvent.Handling msgIn
, subs : model -> Platform.Sub.Sub msgIn
}
Service Component Type
A layout component can render other components using their PID as a reference. The difference comparing to a UI component is the view function.
{ init : PID -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, update : msgIn -> model -> ( model
, List msgOut
, Platform.Cmd.Cmd msgIn )
, view : (msgIn -> msg) -> model -> (PID -> Html msg) -> Html msg
, onSystem : SystemEvent -> SystemEvent.Handling msgIn
, subs : model -> Platform.Sub.Sub msgIn
}
Layout Component Type
The view
function of a layout component:
view : (MsgIn -> msg) -> Model -> (PID -> Html msg) -> Html msg
view toSelf model renderPID =
div
[]
[ renderPID model.child
, button [ onClick (toSelf ButtonWasClicked) ] [ text "Button!" ]
]
The view
function has three arguments:
toSelf
is used to wrap all event-handlers from Html.EventsrenderPID
is used to render other components.As you can see, the output type of the view
function is Html msg
. This is
necessary to allow components to be composed. What would the return type be on
renderPID
if they were not mapped to the same type?
There is no native elm module for a Tuple with three arguments.
mapFirst : (input -> out) -> ( input, x, y ) -> ( out, x, y )
Map the first argument (Model).
mapSecond : (input -> out) -> ( x, input, y ) -> ( x, out, y )
Map the second argument (List MsgOut).
mapThird : (input -> out) -> ( x, y, input ) -> ( x, y, out )
Map the third argument (Cmd).
andThen : (model -> ( model, List msgOut, Platform.Cmd.Cmd msgIn )) -> ( model, List msgOut, Platform.Cmd.Cmd msgIn ) -> ( model, List msgOut, Platform.Cmd.Cmd msgIn )
Run a series of updates on the model
The msgOut's and Cmd's will be composed using System.batch
and
Cmd.batch
.
( model, [], Cmd.none )
|> Component.andThen doSomethingWithModel
addOutMsg : msg -> ( x, List msg, y ) -> ( x, List msg, y )
Add an out message to the output 3-Tuple.
( model, [], Cmd.none )
|> Component.addOutMsg SomeOutMsg
addCmd : Platform.Cmd.Cmd msg -> ( x, y, Platform.Cmd.Cmd msg ) -> ( x, y, Platform.Cmd.Cmd msg )
Add a Cmd to the output 3-Tuple.
( model, [], Cmd.none )
|> Component.addCmd cmd
toCmd : msg -> Platform.Cmd.Cmd msg
Convert a msg to Cmd.
toCmdWithDelay : Basics.Float -> msg -> Platform.Cmd.Cmd msg
Convert a msg to Cmd with a timeout in milliseconds.
Sometimes you'd want to put messages in a queue. Maybe your component is in a state where it can't process them at this point, eg. an InitState.
Store messages in a queue.
emptyQueue : Queue msgIn
Create an Empty Queue
{ model
| queue = Component.emptyQueue
}
addToQueue : msgIn -> Queue msgIn -> Queue msgIn
Add a msg to the queue
{ model
| queue = Component.addToQueue msgIn model.queue
}
runQueue : Queue msgIn -> (msgIn -> model -> ( model, List msgOut, Platform.Cmd.Cmd msgIn )) -> ( model, List msgOut, Platform.Cmd.Cmd msgIn ) -> ( model, List msgOut, Platform.Cmd.Cmd msgIn )
Run the update
function on all messages in the queue
and compose all output.
( model, [], Cmd.none )
|> Component.runQueue queue update