I wrote a simple program based on time example, to test what data are in events. It decodes JSON to value then encodes it back to JSON, then show it in SVG text element. And the only thing I get is {"isTrusted":true}.
Why that happens? How do I get another data of event? I'm using Firefox 49 and online compiler:
import Html exposing (Html)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Svg.Events exposing(on)
import Json.Decode as Json
import Json.Encode exposing (encode)
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model = String
init : (Model, Cmd Msg)
init =
("No event", Cmd.none)
-- UPDATE
type Msg
= Event String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Event event ->
(event, Cmd.none)
subscriptions model = Sub.none
stringifyEvent: Json.Encode.Value -> Msg
stringifyEvent x =
Event (encode 2 x)
-- VIEW
view : Model -> Svg Msg
view model =
svg [ viewBox "0 0 300 300", width "300px", on "mousedown" (Json.map stringifyEvent Json.value) ] [
text_ [x "0", y "30"] [text model]
]
When I try in console
svgElement.addEventListener('click', function(e) {console.log(e)})
It works with all the attributes.
I do not know a way to achieve your goal.
But I can give you an answer why it does the way you described.
If you look at the source code, you'll find that Elm runtime uses
JSON.stringify() for converting Value to String.
And guess what...
svgElement.addEventListener('click', function(e) {console.log(JSON.stringify(e))})
will give you {"isTrusted":true} when you click...
Related
I am making a program that changes the rendered text on the screen to be whatever the user inputs in a text box. I think I have the model and the update part of the elm architecture correct, but I really don't understand the view portion.
I'm just having trouble wrapping my head around the square bracket view functions.
Anyway, I am getting this error.
This div call produces:
Html #(Model -> Model)#
But the type annotation on view says it should be:
Html #Msg#Elm
But I am not sure how to change my view function to return Html Msg and I am kinda confused between the difference between that and a string.
Thank you everyone!
Here is my code ...
module Main exposing (..)
import Browser
import Html exposing (Html, div, text, input, Attribute)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
main =
Browser.sandbox { init = init, update = update, view = view }
type alias Model = String
init : Model
init = "Hello, World!"
type alias Msg = String
update : Msg -> Model -> Model
update msg model =
msg
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "Input new string", value model, onInput update ] []
, div [] [ text model ]
]
You're passing the update function as an argument to onInput. Your probably meant to pass it a Msg, which the runtime will then pass to the update function.
Since your Msg type is an alias for String, you can use onInput identity
Can't quite figure out how to go about sending messages between modules when working with reusable components.
I have an expanding text area that I'd like to use on a number of different sections on a site. The Text Area accepts a portion of HTML that makes up the user actions. Typically handling submit, cancel, upload icons, etc..
Tried to write up a quick example of what I'm talking about without throwing a ton of code on here. So essentially, I'd like to just plug and play peices of HTML that are already attached.
I'm assuming CancelNote is getting fired as a TextArea msg, so it never sees a Cancel Note msg. Not sure how I would use Html.map here (or even if I would).....feel like plug and play method is probably a bad approach, but not sure how else I could achieve decent reusability .
SEPERATE MODULE
update model msg =
case msg of
CancelText ->
( { model | note = (Just "") }
, Cmd.none
)
view: stuff
view stuff =
......
TextArea.view
(button [ Html.Events.onClick CancelText] [])
TEXT AREA MODULE
view : Html.Html msg -> Html msg
view actionHtml =
div [ class "extended_text_area_container" ] [
textarea [] [
]
, actionHtml
]
Messages are just values like any other. You can pass them around directly:
-- SEPERATE MODULE
view: stuff
view stuff =
......
TextArea.view CancelText
-- TEXT AREA MODULE
view : msg -> Html msg
view msg =
div [ class "extended_text_area_container" ]
[ textarea [] []
, button [ onClick msg ] []
]
Edit: If you need to also maintain internal state, just use another message to tell the parent to update the state:
-- Main module
type msg =
...
SetTextAreaState TextArea.state
update model msg =
case msg of
...
SetTextAreaState state ->
{ model | textAreaState = state }
view : Model -> Html msg
TextArea.view SetTextAreaState model.textAreaState
-- TextArea module
type State =
...
type Msg =
Clicked
update : State -> Msg -> State
update state msg =
case msg of
Clicked ->
{ state | clicked = True }
view : (State -> msg) -> State -> Html msg
view toMsg state =
let
updateAndWrap msg =
toMsg (update state msg)
in
div [ class "extended_text_area_container" ]
[ textarea [] []
, button [ onClick (updateAndWrap Clicked) ] []
]
Here, instead of passing a msg to onClick directly in TextArea.view, call a function that updates the state and then wraps it in the msg constructor passed in from the parent, which will produce a message of a type that we don't know anything about.
Also, while I use an internal Msg type and update function similarly to the overall Elm architecture, that is in no way mandatory. It's just a nice way of doing it since it's familiar and scales well.
So I started learning Elm today. I use VSCode as my editor.
I followed the docs for the set up and installed elm as well as el-format via npm install -g elm elm-format. I also installed the VSCode Elm extension.
Next, in my settings.json I set:
"[elm]": {
"editor.formatOnSave": true
},
Then I went on with the tutorial. In it the code is formatted like this:
import Browser
import Html exposing (Html, Attribute, div, input, text)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
{ content : String
}
init : Model
init =
{ content = "" }
-- UPDATE
type Msg
= Change String
update : Msg -> Model -> Model
update msg model =
case msg of
Change newContent ->
{ model | content = newContent }
-- VIEW
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "Text to reverse", value model.content, onInput Change ] []
, div [] [ text (String.reverse model.content) ]
]
But when I hit safe, it formats the code like this:
module Main exposing (Model, Msg(..), init, main, update, view)
import Browser
import Html exposing (Attribute, Html, div, input, text)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
{ content : String
}
init : Model
init =
{ content = "" }
-- UPDATE
type Msg
= Change String
update : Msg -> Model -> Model
update msg model =
case msg of
Change newContent ->
{ model | content = newContent }
-- VIEW
view : Model -> Html Msg
view model =
div []
[ input [ placeholder "Text to reverse", value model.content, onInput Change ] []
, div [] [ text (String.reverse model.content) ]
]
So it adds extra lines, and extra module Main exposing ... and doubles the number of spaces. I tried setting spaces to 2 again using the footer in VSCode, but that didn't help.
My questions are:
Is it okay that saving adds the extra module Main ...?
Is having 2 spaces best practice / community standard, or 4?
If it is 2 (like it is in the tutorial's default code) how can I get my formatter to respect that?
If it is not, why has the tutorial the non-standard indentation?
First of all, this question is both too broad and primarily opinion-based and will probably be closed because of that. It would have been more suited for the forums I think.
That said, I'm going to try to answer it as best as I can anyway, since it's here:
Yes? Most modules won't be very useful without exposing something and it's good practice to be explicit about what's being exposed.
elm-format is the community standard, so 4 it is.
You can't. This is by design. It has also been discussed to death in various fora. Here's one issue discussing it
You'd have to ask Evan about that. It might be related to formatting for the web, or just Evan being lazy.
The Elm getting started guide doesn't seem to use the standard Elm HTML events and I'm not sure how to use them. For example, onBlur:
view model =
div []
[ div []
input [ type_ "text", onInput MsgA ] []
, input [ type_ "text", onBlur MsgB ] []
]
This fails in the compiler because now I am returning type Html (String -> Msg) on line 3 but Html (Msg) in line two.
Why are these two events incompatible? Is there any way to use both at the same time? Additionally, the docs don't make it clear enough for someone like me to understand how to use onBlur.
onBlur has a different type signature than onInput.
onInput : (String -> msg) -> Attribute msg
onBlur : msg -> Attribute msg
That means whatever Msg you use for onInput has to take a single string parameter. Likewise, the Msg used in onBlur cannot take a parameter. If you redefined MsgA and MsgB to the following, it would compile:
type Msg
= MsgA String
| MsgB
Edit
If you wanted your blur handler to be able to accept the target value, you can roll your own like this:
import Html.Events exposing (on, targetValue)
import Json.Decode as Json
onBlurWithTargetValue : (String -> msg) -> Attribute msg
onBlurWithTargetValue tagger =
on "blur" (Json.map tagger targetValue)
I have implemented the HTTP service request using elm architecture.but i am also want to implement any loading gif image so that user will know that something is happen in the back ground.
can any please help me to implement to this implementation.
You can add a property in your model, whether it is loading or not.
Then you can let your view reflect the status by show spinner or text, or whatever.
In case of the example at http example, you can modify
following code, for example by adding iswaiting property in the Model ...
In this example, FetchSucceed message gets fired when ajax call is complete.
type alias Model =
{ topic : String
, gifUrl : String
, iswaiting : Bool
}
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
MorePlease ->
({ model | iswaiting = True }, getRandomGif model.topic)
FetchSucceed newUrl ->
(Model model.topic newUrl False, Cmd.none)
FetchFail _ ->
(model, Cmd.none)
view : Model -> Html Msg
view model =
div []
[ h2 [] [text model.topic]
, text <| if model.iswaiting then "waiting" else ""
, button [ onClick MorePlease ] [ text "More Please!" ]
, br [] []
, img [src model.gifUrl] []
]
You'll have to adjust other parts as well.
But, I hope you can see what is going on.
You can add a loading gif image on body and make it appear on top of the page by z-index and after http request is completed then delete that image from body.