Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Original post
*original post*
I've come across the same problem in the past, thought about it a few times already but never came up with a proper solution.
Probably the approach by @rupert and @pdamoc are the best ones.
One other alternative would be to allow Main.elm peek into the Msg of the child pages and intercept some of them. I realize this is not the cleanest thing to do but it's the tradeof.
(note: this is just an approach I drafted out quickly as an example for this post and I haven't really tried it in a real application)
*Main.elm*
```
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SomePageMsg (SomePage.AuthFailMsg failType) ->
case failType of
AuthMsg.Unauthenticated -> handleUnauthenticated model
AuthMsg.Unauthorized -> handleUnauthorized model
SomePageMsg somePageMsg ->
-- standard dispatching here: SomePage.update ...
```
In `SomePage.elm` I changed a few things
- Expose `Msg(..)` instead of `Msg` (not ideal but I'm not exposing the internal messages, see code below)
- Split up the Msg type into a plain message type and an http-with-authentication message type
```
module SomePage exposing (Model, Msg(..), init, update, view)
...
type Msg
= PlainMsg PlainMsg
| AuthFailMsg AuthMsg.AuthFailure -- AuthFailure will be reused across all child pages but basically it's Unauthenticated (401) or Unauthorized (403)
| AuthSuccessMsg AuthSuccessMsg
type PlainMsg = ... -- These are the regular messages FetchData, SetFieldValue, ...
type AuthSuccessMsg -- HTTP Result which succeeded authentication and authorization (but might still fail on something else)
= RequestPerformed (Result Http.Error BasicAuthResponse)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
PlainMsg submsg ->
...
-- Handled in Main.elm
AuthFailMsg _ ->
( model, Cmd.none )
-- 'Ok' processing
AuthSuccessMsg (RequestPerformed (Ok response)) ->
...
-- Any other HTTP error (Timeout, HTTP 500, ...)
-- (or fallback in case Main does not handle the message)
AuthSuccessMsg (RequestPerformed (Err error)) ->
...
```
It does mean that I need to wrap the messages in this page:
*SomePage.elm (C'ud)*
```
view : Model -> Html Msg
view model =
...
Html.button
[ Html.onClick (PlainMsg NoAuthRequest) ]
[ ... ]
-- Note the `withAuth`
someHttpGet : String -> Cmd Msg
someHttpGet url =
Http.get
{ url = url
, expect = Http.expectJson (withAuth RequestPerformed) basicAuthResponseDecoder
}
```
The `someHttpGet` function uses a helper `withAuth` to wrap the message
*SomePage.elm (C'ud)*
```
withAuth :
(Result Http.Error a -> AuthSuccessMsg)
-> Result Http.Error a
-> Msg
withAuth =
AuthMsg.withAuth AuthFailMsg AuthSuccessMsg
```
Which itself is a specific version of a more general `withAuth` function
*AuthMsg.elm*
```
module AuthMsg exposing (AuthFailure(..), withAuth)
{-| This function wraps a message with unauthenticated / unauthorized checks
-}
withAuth :
(AuthFailure -> msg) -- ^ The message constructor in case of failure (unauthenticated | unauthorized)
-> (authMsg -> msg) -- ^ The message constructor in case of success
-> (Result Http.Error a -> authMsg) -- ^ The sub-message constructor
-> Result Http.Error a -- ^ The HTTP result
-> msg
withAuth failMsg successMsg toAuthMsg result =
if isUnauthenticated result then
failMsg Unauthenticated
else if isUnauthorized result then
failMsg Unauthorized
else
successMsg <| toAuthMsg result
type AuthFailure = Unauthenticated | Unauthorized
```
Sorry for the lengthy post..
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.