Currently in TEA (The Elm Architecture) business logic (Send API request after button has been clicked, and etc...) and UI logic (Rippling button effect, dropdown animation, and etc...) is combined together and should be handled simultaneously.
These logics have great potential to become separated from each other and make developers to focus on one logic each time.
A great example of headache in TEA are UI libraries. Their goal is giving users nice implemented UI logics but because of TEA we cannot separate UI logic from business logic so developers should handle those logics by themself.
Summary
Monarch element is a solution for TEA to make separated UI logics possible alongside a pure functional architecture.
In Monarch each document have an input and output which are the bridges for bidirectional communication with the outside of the document.
Monarch element is a derivation of Monarch document which is responsible to translate DOM attributes and properties to input of the document and outputs of the document to DOM events.
This translation mechanism makes an abstraction over web components and custom elements which has recently developed as a web standard by W3C.
Basic Example
Here I introduce a toggle custom HTML element named x-toggle as an example.
type Model = { checked :: Boolean
, disabled :: Boolean
, value :: String
}
data Message = MonarchSentInput Input
| UserToggledInput
type OptionalInput r = { disabled :: Boolean | r }
type Input = OptionalInput (value :: String)
type Output = Variant (OnChange ())
defaultInput :: OptionalInput ()
defaultInput = { disabled: false
}
init :: Input -> Model
init = Record.merge { checked: false }
update :: Message -> Model -> Model
update message model = case message of
UserToggledInput -> model { checked = not model.checked }
MonarchSentInput input -> Record.merge input model
type Slots = (label :: RequiredHtmlSlot)
view :: Model -> HostHtml Slots Message
view { checked, disabled } =
host_ [ label { onClick: const UserToggledInput }
[ input { checked
, disabled
, type: InputType.Radio
}
, span_ [ slot' (SProxy "children") ]
]
]
command :: Message -> Model -> Command () Message Output Unit
command message model = case message of
UserToggledInput -> raise $ onChange model.checked
_ -> pure unit
subscription :: Upstream Input Model Message -> Event Message
subscription { eInput } = map MonarchSentInput eInput
type OnChange r = (onChange :: CustomEvent Boolean | r)
onChange :: forall r. Boolean -> Variant (OnChange r)
onChange detail =
Monarch.mkCustomEvent { detail
, name: SProxy :: SProxy "onChange"
, bubbles: true
}
Details
Input
Inputs of a web component divides into two category:
Attribute: can be provides in HTML documents and it can contain only scalar types.
Property: provides through JavaScript DOM API and it can contain any sort of value.
Monarch deals differently with these concepts. A Monarch document have only one way to get information from outside which is input type. In element the input type is a record:
type Input = { a :: String
, b :: Array String
}
Imagine the above input type is for an element named x-element. Every field of the input record will be available as a property through DOM API of the element:
const element = document.createElement('x-element')
// Get
element.a
element.b
// Set
element.a = 'foo'
element.b = ['x', 'y']
But how Monarch decides which one of the input record fields can be an attribute? Based on the type of the field! If the type of the field be an instance of the Attributative typeclass, Monarch reflect it as an attribute too.
class Attributative a
instance stringAttributative :: Attributative String
instance numberAttributative :: Attributative Number
instance intAttributative :: Attributative Int
instance booleanAttributative :: Attributative Boolean
In the above example the a field can become an attribute because it's of type String which has an Attributative instance. But the b field can't because its type is Array String which doesn't have Attributative instance. So the x-element can be used in HTML like the following:
<x-element a="foo">
But the x-toggle implementation was not that simple! Let's look at it:
type OptionalInput r = { disabled :: Boolean | r }
type Input = OptionalInput (value :: String)
defaultInput :: OptionalInput ()
defaultInput = { disabled: false
}
Simplified version of Input type for x-toggle is:
type Input = { disabled :: Boolean, value :: String }
As you maybe already guessed above input type leads into two DOM properties named disabled and value and two HTML attributes named disabled and value.
There are some cases we want to provide a default value for some inputs and make them optional. In this case we want to make disabled input optional and define default false value for it. So we defined the optional part of the input into OptionalInput type which has default values in defaultInput.
It's valuable to point out all of the required inputs should be provided before attachment of the element to the DOM. Otherwise it leads to an exception!
Model
Each element like a document has a model and message type:
type Model = { checked :: Boolean
, disabled :: Boolean
, value :: String
}
data Message = MonarchSentInput Input
| UserToggledInput
The first model construction made upon the first input of the element through init function:
After that, developer can decide to respond to the input changes or not by subscribing to the input event eInput which is accessible in the Upstream of the subscriptions:
The model change only can take place in the update function.
update :: Message -> Model -> Model
update message model = case message of
UserToggledInput -> model { checked = not model.checked }
MonarchSentInput input -> Record.merge input model
View
WIP
Output
WIP
Flags
[x] I would like to submit a PR if the requested feature becomes approved (Help can be provided if you need assistance submitting a PR.).
RFC: Monarch element API
Motivation
Currently in TEA (The Elm Architecture) business logic (Send API request after button has been clicked, and etc...) and UI logic (Rippling button effect, dropdown animation, and etc...) is combined together and should be handled simultaneously. These logics have great potential to become separated from each other and make developers to focus on one logic each time. A great example of headache in TEA are UI libraries. Their goal is giving users nice implemented UI logics but because of TEA we cannot separate UI logic from business logic so developers should handle those logics by themself.
Summary
Monarch
element
is a solution for TEA to make separated UI logics possible alongside a pure functional architecture. In Monarch eachdocument
have an input and output which are the bridges for bidirectional communication with the outside of thedocument
. Monarchelement
is a derivation of Monarchdocument
which is responsible to translate DOM attributes and properties to input of thedocument
and outputs of thedocument
to DOM events. This translation mechanism makes an abstraction over web components and custom elements which has recently developed as a web standard by W3C.Basic Example
Here I introduce a toggle custom HTML element named
x-toggle
as an example.Usage
Implementation
Details
Input
Inputs of a web component divides into two category:
Monarch deals differently with these concepts. A Monarch
document
have only one way to get information from outside which isinput
type. Inelement
theinput
type is a record:Imagine the above
input
type is for an element namedx-element
. Every field of theinput
record will be available as a property through DOM API of the element:But how Monarch decides which one of the
input
record fields can be an attribute? Based on the type of the field! If the type of the field be an instance of theAttributative
typeclass, Monarch reflect it as an attribute too.In the above example the
a
field can become an attribute because it's of typeString
which has anAttributative
instance. But theb
field can't because its type isArray String
which doesn't haveAttributative
instance. So thex-element
can be used in HTML like the following:But the
x-toggle
implementation was not that simple! Let's look at it:Simplified version of
Input
type forx-toggle
is:As you maybe already guessed above input type leads into two DOM properties named
disabled
andvalue
and two HTML attributes nameddisabled
andvalue
. There are some cases we want to provide a default value for some inputs and make them optional. In this case we want to makedisabled
input optional and define defaultfalse
value for it. So we defined the optional part of the input intoOptionalInput
type which has default values indefaultInput
. It's valuable to point out all of the required inputs should be provided before attachment of the element to the DOM. Otherwise it leads to an exception!Model
Each
element
like adocument
has a model and message type:The first model construction made upon the first
input
of the element throughinit
function:After that, developer can decide to respond to the input changes or not by subscribing to the input event
eInput
which is accessible in theUpstream
of thesubscriptions
:The model change only can take place in the
update
function.View
WIP
Output
WIP
Flags