Hi all
I wrote the following code, but how does it work, it seems for me a mystery:
port module Security.Data exposing (..)
import Security.Env as Env
import Security.Keycloak as Keycloak
import Browser.Navigation as Nav
import Url
import Json.Decode exposing (..)
import Http
port doPubKc : () -> Cmd msg
port onSubKc : (Value -> msg) -> Sub msg
type alias Model = {
env: Env.Model,
kc: Keycloak.Model
}
init : Model
init = Model Env.init Keycloak.init
type Msg
= OnEnv (Result Http.Error String)
| OnSubKc Keycloak.Token
update : Url.Url -> Nav.Key -> Msg -> Model -> (Model, Cmd Msg)
update url key msg model =
case msg of
OnEnv res ->
case res of
Ok succ ->
({model | env = succ}, Cmd.none)
Err fail ->
(model, Nav.pushUrl key (Url.toString (buildErrorUrl url)))
OnSubKc value ->
case value of
Ok succ ->
({model | kc = succ }, Env.reqEnv OnEnv )
Err fail ->
(model, Nav.pushUrl key (Url.toString (buildErrorUrl url)))
buildErrorUrl : Url.Url -> Url.Url
buildErrorUrl old =
Url.Url old.protocol old.host old.port_ "/error" old.query old.fragment
As you can see, when an error occurs, it will redirect to /error
path and it will trigger the onUrlChange
event.
The onUrlChange
will be caught in the Main.elm
file, that contains the following code:
module Main exposing (Model, Msg(..), init, main, update, view)
import Browser
import Browser.Navigation as Nav
import Home.Page as Home
import Html exposing (..)
import Html.Attributes exposing (..)
import Json.Decode as Decode
import NotFound.Page as NotFound
import Url.Parser as Parser exposing (..)
import Url
import Http
import Menu.Page as Menu
import Security.Data as Security
import Security.Keycloak as Keycloak
import Error.Page as Error
---- MODEL ----
type Page
= NotFound NotFound.Model
| Error Error.Model
| Home Home.Model
-- | AccountPage AccountPage.Model
type alias Model =
{ url : Url.Url
, key : Nav.Key
, security : Security.Model
, page : Page
}
init : Decode.Value -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
(Model url key Security.init (Home "Hello Foo"), Cmd.none)
---- UPDATE ----
type Msg
= OnUrlChange Url.Url
| OnUrlRequest Browser.UrlRequest
| OnSecurity Security.Msg
| NotFoundMsg NotFound.Msg
| ErrorMsg Error.Msg
| HomeMsg Home.Msg
| MenuMsg Menu.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
OnUrlRequest urlRequest ->
case urlRequest of
Browser.Internal url ->
( model, Nav.pushUrl model.key (Url.toString url) )
Browser.External href ->
( model, Nav.load href )
OnUrlChange url ->
navigate url model
OnSecurity secMsg ->
handleSecurity model (Security.update model.url model.key secMsg model.security)
HomeMsg homeMsg ->
case model.page of
Home homeModel ->
navToHome model (Home.update homeMsg homeModel)
_ ->
(model, Cmd.none)
NotFoundMsg notFoundMsg ->
case model.page of
NotFound notFoundModel ->
navToNotFound model (NotFound.update notFoundMsg notFoundModel)
_ ->
(model, Cmd.none)
ErrorMsg errorMsg ->
(model, Cmd.none)
MenuMsg menuMsg ->
(model, Cmd.none)
-- UPDATE HANDLERS --
handleSecurity : Model -> (Security.Model, Cmd Security.Msg) -> (Model, Cmd Msg)
handleSecurity model (secModel, secMsg) =
({model | security = secModel}, Cmd.map OnSecurity secMsg)
--- NAVIGATION ---
navToHome : Model -> (Home.Model, Cmd Home.Msg) -> (Model, Cmd Msg)
navToHome model (homeModel, homeMsg) =
({model | page = Home homeModel},
Cmd.map HomeMsg homeMsg )
navToNotFound : Model -> (NotFound.Model, Cmd NotFound.Msg) -> (Model, Cmd Msg)
navToNotFound model (notFoundModel, notFoundMsg) =
({model | page = NotFound notFoundModel},
Cmd.map NotFoundMsg notFoundMsg )
navToError : Model -> (Error.Model, Cmd Error.Msg) -> (Model, Cmd Msg)
navToError model (errorModel, errorMsg) =
({model | page = Error errorModel},
Cmd.map ErrorMsg errorMsg)
navigate : Url.Url -> Model -> (Model, Cmd Msg)
navigate url model =
let
parser =
oneOf [
route top ( navToHome model Home.init),
route (Parser.s "error") (navToError model Error.init)
]
in
case Parser.parse parser url of
Just page ->
page
Nothing ->
navToNotFound model (NotFound.init "Can not find the page")
route : Parser a b -> a -> Parser (b -> c) c
route parser handler =
Parser.map handler parser
--- SUBSCRIPTIONS ---
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch [
Sub.map OnSecurity (Security.onSubKc (\value -> Security.OnSubKc (Keycloak.validate value)))
]
---- VIEW ----
content : Model -> Html Msg
content model =
main_ []
[ case model.page of
Home homeModel ->
Html.map HomeMsg (Home.view homeModel)
Error errorModel ->
Html.map ErrorMsg (Error.view errorModel)
NotFound notFoundModel ->
Html.map NotFoundMsg (NotFound.view notFoundModel)
]
body : Model -> List (Html Msg)
body model =
[ Html.map MenuMsg Menu.content,
content model
]
view : Model -> Browser.Document Msg
view model =
{ title = "Cockpit"
, body = body model
}
---- FUNCTIONS ----
---- PROGRAM ----
main : Program Decode.Value Model Msg
main =
Browser.application
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
, onUrlChange = OnUrlChange
, onUrlRequest = OnUrlRequest
}
The question is, how does the event OnUrlChange
get caught in the Main.elm
, since OnUrlChange
does not get caught in the update
function in Security.Data
module and it is wrapped with OnSecurity
.
Thanks