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.
Related
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.
I have a feature called Editor that I'm trying to plug into my app. Its view returns a the type Html EditorMsg. I plug it in here:
edit : Editor.Types.Model -> Html EditorMsg
edit editorModel =
div [ class "edit" ] [ Editor.view editorModel ]
In the app, I have routing and so edit is called by way of my main view function, plus a couple of functions that manage which route to show:
-- VIEW
view : Model -> Html Msg
view model =
div []
[ RoutingMsg navBar
, EditorMsg (render model)
]
render : Model -> Html EditorMsg
render model =
case model.route of
Nothing ->
li [] [ text "Invalid URL" ]
Just route ->
showRoute route model
showRoute : Route -> Model -> Html EditorMsg
showRoute route model =
case route of
Home ->
home
Editor ->
edit model.editor
My view also contains a navBar as you can see, and that returns Html possibly containing a different type of message, RoutingMsg:
navBar : Html RoutingMsg
navBar =
NavBarState.config
|> NavBarState.items
[ navItem "/" "Home" False
, navItem "/editor" "Edit" False
]
|> NavBar.view
This setup didn't compile because in view I have a list of two different types, one returning Html RoutingMsg and the other Html EditorMsg, so I set up a union type to try to contain that difference:
type Msg
= RoutingMsg (Html RoutingMsg)
| EditorMsg (Html EditorMsg)
This seems to work, but then I run into trouble in my update method, where the matching doesn't quite work. Long and short of it is that it feels as though I've just got the wrong pattern. My goal was to make my Editor somewhat independent, like a module, that can be plugged in different places. But it's hard for me to understand in Elm, how to integrate things.
To illustrate the problem more simply, I created this example on ellie-app that approximates what I tried to do but can't get working: https://ellie-app.com/PKmzsV3PC7a1/1
Is my approach here just incorrect, or if not, how can I get this code to work?
You should use Html.map to map children messages to the top-level Msg
Here's what you've been missing:
view : Model -> Html Msg
view model =
div []
[ Html.map ButtonDecMsg buttonDec
, div [] [ text (toString model) ]
, Html.map ButtonIncMsg buttonInc
]
Also the type annotation definition of child update functions should include the message type:
buttonDecUpdate : ButtonDecMsg -> Model -> Int
buttonDecUpdate msg model =
model - 1
Here is an example of working app: https://ellie-app.com/PM4H2dpFsfa1/0
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...
I've created a button that holds a boolean value and when you click it, it changes this value and text inside the button. The code below works but I have a feeling I'm doing an overkill. How can I write this button as simple as possible but still following the ELM architecture?
module BtnPin where
import Html exposing ( Html )
import Html.Events as E
import StartApp.Simple as StartApp
-- MAIN
main =
StartApp.start { model = emptyModel, view = view, update = update }
-- MODEL
type alias Model =
{pinned : Bool}
init : Model
init = Model False
emptyModel : Model
emptyModel =
{ pinned = False
}
pin : Model -> Model
pin model =
if model.pinned then
Model False
else
Model True
viewPin : Signal.Address Action -> Model -> Html
viewPin address model =
if model.pinned == True then
Html.button
[ E.onClick address Pin ]
[ Html.text <| "Unpin" ]
else
Html.button
[ E.onClick address Pin ]
[ Html.text <| "Pin" ]
-- UPDATE
type Action = Pin
update : Action -> Model -> Model
update action model =
pin model
-- VIEW
view : Signal.Address Action -> Model -> Html
view address model =
Html.div []
[ viewPin address model ]
The more explicit you are, and the closer you stay to the Elm Architecture, the easier it is to read for someone who knows the pattern. It's also easy to extend. But of course you can simplify a component that you don't expect to change.
Just remember that with the code you have in your question, the only documentation you need is the one-sentence description, because the code is Elm Architecture, everything has type annotations and is super simple. When you condense your code, you may need more documentation.
A simplified button:
module BtnPin where
import Html exposing ( Html )
import Html.Events as E
import StartApp.Simple as StartApp
main =
StartApp.start { model = False, view = view, update = always not }
-- VIEW
view : Signal.Address () -> Bool -> Html
view address model =
Html.div [] [ viewPin address model ]
viewPin : Signal.Address () -> Bool -> Html
viewPin address model =
let
text = if model then "Unpin" else "Pin"
in
Html.button
[ E.onClick address () ]
[ Html.text text ]
This may be a bit extreme, so you can compromise by defining an update function, a Model type and an Action type. But then you're pretty close to where you started...
I am new to Elm, probably have an incorrect understanding of the architecture and this may be an XY question (but I wouldn't know yet...)
Anyways, I am trying to create a url-routed SPA in Elm. I am using evancz/start-app to generate individual dynamic pages using the elm-achitecture-tutorial example 1 as a starting point for each 'page':
Frontend.Pages.Home.display : Signal Html
Frontend.Pages.Home.display = StartApp.start
{ model = 0
, update = update
, view = view
}
type alias Model = Int
type Action = Increment | Decrement
update : Action -> Model -> Model
update action model =
case action of
Increment -> model + 1
Decrement -> model - 1
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ button [ onClick address Decrement ] [ text "-" ]
, div [ countStyle ] [ text (toString model) ]
, button [ onClick address Increment ] [ text "+" ]
]
I am using TheSeamau5/elm-router to
route : String -> Signal Html
route = Router.match
[ "/" :-> Frontend.Pages.Home.display
] Frontend.Pages.Errors.FourOhFour.display
I am using TheSeamau5/elm-history to gather the latest url:
main : Signal Html.Html
main = Frontend.Routes.route History.path
Clearly this is the error; I am passing a Signal String into a method that takes a String. The problem is that if I use the following:
main = Signal.map Frontend.Routes.route History.path
which I would expect Signal.map Frontend.Routes.route History.path to be of type Signal Html, instead the compiler complains of a conflict:
The type annotation for `main` does not match its definition.
7| main : Signal Html.Html
^^^^^^^^^^^^^^^^ As I infer the type of values flowing through your program, I see a conflict between these two types:
Html.Html
Signal Html.Html
What's going on here? It seems like Signal.Map is "unpacking" the Signal Html. Is this the case?
It seems my core question, at any rate, is: How can I present a single-page-app, routed to separate reactive 'pages' based on a Signal String while not reimplementing Router?
Thanks in advance.
I believe you are ending up with a final result of Signal (Signal Html) and main is expecting Signal Html. I believe the error you are receiving is specifically for the type it is expecting for Signal a. It is saying, I was expecting a to be Html but in fact I found it to be Signal Html.
Here are the types we have:
Signal.map : (a -> result) -> Signal a -> Signal result
Frontend.Routes.route : String -> Signal Html
Historypath : Signal String
Then we have Signal.map Frontend.Routes.route which has the type Signal String -> Signal (Signal Html).
We then apply History.path giving us the final result Signal (Signal Html)
If you change route to return Html rather than Signal Html you should be good to go.