I'm making a simple page with multiple textarea elements in Elm. I'm struggling to get the data saved and especially identifying which textarea was updated. Maybe an example illustrates the point better
I have multiple elements made in view from a list
type alias Model = { List Comment, ... }
type alias Comment = {id: Int, content: String, draftContent: String, ...}
type Event = Save Comment | SaveDraft String
-- view
model.comments
|> List.map( --iterate the list of comments and render html
div [attrubute "name" "comment"] [
textarea [onInput SaveDraft] [text comment.content],
button [onClick (Save comment)] [text "Post comment"]
]
-- update
case event of
Save comment ->
-- Replace the comment content with draft data and clear draft
SaveDraft draftText ->
-- Update the draft content with text from event
-- Which Comment is it?
Based on examples here I came up with the idea of sending each textarea input as an event to update function and saving the draft data.
Now the problem here is that onInput only accepts types with a String parameter and I have no means of identifying which one of the comments was modified.
Change the Event union type to include the comment (SaveDraft String --> SaveDraft Comment String)
type Event = Save Comment | SaveDraft Comment String
-- view
model.comments
|> List.map( --iterate the list of comments and render html
div [attrubute "name" "comment"] [
textarea [onInput (SaveDraft comment.content)] [text comment.content],
button [onClick (Save comment)] [text "Post comment"]
]
Currying causes (SaveDraft comment.content) to have the same return value as before
Related
I am trying to access an elements HTML properties when clicked. I figured out that Events.onClick will only return event.target.value and I need to use Events.on to handle custom behaviours.
What I need is: when click on a div I should be able to access its HTML properties, for now id and name and send this value to update with some message.
I tried like this:
onClickData : msg -> Attribute msg
onClickData handler =
on "click" (Decode.succeed handler )
---
view: ...
....
div[onClickData HandleClick] []
This way i am able to trigger HandleClick action when the div is clicked but cannot access its HTML properties.
As #glennsl has noted, you normally would want to do something more like this:
view identification =
button [ id identification, onClick (MyMsg identification) ]
[ text "Click me"
]
i.e. you can pass data straight into your Msg type.
That said, there are some unusual inter-op situations where you might want to do this. The general principle is that you can get the element that you bound your event handler to from event.currentTarget, so you can use the following decoder:
decodeProperty : String -> Decoder a -> Decoder a
decodeProperty property decoder =
Decode.at ["currentTarget", property] decoder
--- use
onClickId : (String -> msg) -> Attribute msg
onClickId tagger =
on "click" (Decode.map tagger (decodeProperty "id"))
How do I get the caption from a button?
decorateOn : String -> Html Msg -> Html Msg
decorateOn selectedCaption button =
if button.text == selectedCaption then
button [ class "selectedNavigationButton" ] []
else
button [ class "navigationButton" ] []
button does not have a field named text. - The type of button
is:
Html Home.Msg
Which does not contain a field named text.
Note, I realize that the "button" is really of type Html Msg.
You need to turn your thinking on its head. Rather than seeing what is in the button text, you need to set the text at the same stage as setting the class. So that gives you something like
decorateOn : String -> Html Msg -> Html Msg
decorateOn selectedCaption button =
if selectedCaption == "the selected value" then
button [ class "selectedNavigationButton" ] [text selectedCaption ]
else
button [ class "navigationButton" ] [text selectedCaption]
You can't get the text from a button without resorting to hacks involving ports and JavaScript. Moreover, you can't really inspect anything about the Elm Virtual DOM from within Elm.
Instead, try to refactor your app so that you can get the information from your model.
I am experimenting with an Elm app in which I wish to generate actions from clicking within a div, but I wish the actions to contain the coordinates of the click.
I can see that I can generate actions from clicks like this:
div [onClick address MyAction] [text "click in here"]
But I can't see how to apply a mapping function to the original click data to populate fields in MyAction
You can pull different properties out of the javascript event object using Json Decoders. To get the clientX and clientY values as a tuple, you could create the following decoder:
import Json.Decode as Json exposing ((:=))
eventPos : Json.Decoder (Int, Int)
eventPos =
Json.object2
(,)
("clientX" := Json.int)
("clientY" := Json.int)
In order to do something with those values, you'll need to add an Action that can accept the tuple as a parameter:
type Action
= MyAction
| DivClicked (Int, Int)
Finally, instead of using the onClick function to generate the event handler attribute on your div, you'll need to use the on function, passing in your Json decoder:
on "click" eventPos (Signal.message address << DivClicked)
Here's a gist containing a working example that you can paste right into http://elm-lang.org/try.
I am trying to place my html view in the middle of a container.
Unfortunately, container gives output of type Graphics.Element.Element whereas html view must be of the type, Html.Html.
And also, the html element constructors like div, checkbox, button, body return type is Html whereas, container needs a Graphics.Element parameter.
This is an erroneous html view, which i would like to make it work,
view : Signal.Address Action -> Model -> Element
view address model =
container 1000 1000 middle <|
toForm <|
div []
[ button [ onClick address Decrement ] [text "-"]
, div [ countStyle ] [ text (toString model ) ]
, button [ onClick address Increment ] [ text "+" ]
]
It has two buttons, one div all placed in the middle of a 1000 * 1000 container.
The elm-html package has a fromElement that can be used to create an Html from an Element.
I will say though, the idea with elm-html is to allow for a more "normal" flow of rendering HTML and using CSS to style it. While it would work to take your Html, convert it to an to either a Form or Element, use a collage or container to move it to the center of some area, and then convert back to Html, I'd probably recommend against it. If you're using HTML, just render that and style it (either with an external stylesheet or withHtml.Attributes).
This is the view snippet taken from elm checkbox examples -
view address model =
div [] <|
span [toStyle model] [text "Hello, how are yo?"]
:: br [] []
:: checkbox address model.red Red "red"
++ checkbox address model.underline Underline "underline"
++ checkbox address model.bold Bold "bold"
checkbox : Address Action -> Bool -> (Bool -> Action) -> String -> List Html
checkbox address isChecked tag name =
[ input
[ type' "checkbox"
, checked isChecked
, on "change" targetChecked (Signal.message address << tag)
]
[]
, text name
, br [] []
]
1) I understand, double colons and double plus are for concatenating lists? How are they different from each other?
2) In the checkbox function in this line, (Signal.message address << tag), what does tag unbind to? Is it Red (or) red? What is this argument used for?
3) What is the type of argument does the address function take?
Answer
double colon (::) adds a single element to the start of a list.
double plus (++) concatenates two lists.
tag would be equal to Red in the when checkbox is called from checkbox address model.red Red "red". Red is a function from Bool to Action. It wraps the event of the check box in this data constructor so that later in the update function, you can distinguish the event of that check box from the events of the other check boxes.
address is not a function. It has type Address Action, meaning it holds the address of a mailbox that can receive Action messages. This "mailbox" is not visible in the code, it's used internally by StartApp, which is used in the main function.
Code references
To keep this question useful even if the linked example changes, these are the relevant code portions I'm referring to:
The update function:
update action model =
case action of
Red bool ->
{ model | red <- bool }
Underline bool ->
{ model | underline <- bool }
Bold bool ->
{ model | bold <- bool }
The Action type:
type Action
= Red Bool
| Underline Bool
| Bold Bool
The main function:
main =
StartApp.start { model = initialModel, view = view, update = update }