I am trying to make some conditional routes. The condition resolves on the serverside.
Route rule example:
| (dynamicRouteCT("#user" / long.caseClass[User]) ~> dynRender((page: User) => <.div("Hello, " + page.id.toString)))
.addCondition((page: User) => checkPermissions(page.id))(_ => Some(redirectToPage(Page403)(Redirect.Push)))
checkpermissions
body:
def checkPermissions(id: Long) = CallbackTo.future{
/*Ajax.get(s"http://some.uri/?id=$id") map (res =>
* if (something) true
* else false
* )
*/
//the request before returns Future[XMLHttprequest] witch maps to Future[Boolean]
Future(false)
}
I got type missmatch here: (page: User) => checkPermissions(page.id)
Is it possible to perform ajax request inside conditional routes?
If we look at def addCondition(cond: Page => CallbackTo[Boolean])(condUnmet: Page => Option[Action[Page]]): Rule[Page]
we can see that it requires a CallbackTo[Boolean]
. Because of the nature of the JS env, there is now way to go from Future[A]
to A
. Although it's not a limitation from scalajs-react itself, it is an inherited reality that will affect your scalajs-react code; as this table in the doc shows, there's no way to go from a CallbackTo[Future[Boolean]]
to a CallbackTo[Boolean]
.
This type-level restriction is actually a really good thing for user experience. The router is synchronous, it must determine how to render routes and route changes immediately. If it were allowed to be async and somehow supported Future
s, then the user would experience noticable (and potentially huge) delays without any kind of visual feedback or means of interruption.
The "right way" to solve this problem is to use a model that covers the async state. This is what I would do:
AsyncState[E, A]
ADT with cases: Empty
, AwaitingResponse
, Loaded(value: A)
, Failed(error: E)
.Loaded
, retry callback on Failed
, timeStarted on AwaitingResponse
, etc.)AsyncState[Boolean]
in your (local/client-side) state.Callback
in a for-comprehension to wire things up and satisfy the types.)AsyncState[Boolean]
, render something meaningful to the user. If it's AwaitingResponse
, display a little spinner; if it's failed display an error and probably a retry button.(It should also be noted that AsyncState[Boolean]
shouldn't actually be Boolean
as that's not very descriptive or true to the domain. It would probably be something more meaningful like AsyncState[UserAccess]
or something like that.)
Hope that helps! Good luck!