Reputation: 3173
I am looking for a way to add a prefix to all reverse routes of an html template without wrapping them all in function applications. I planned to use an html base tag but the reverse routes all start with a slash and therefore are relative to the host of the base tag rather than the full URL. Is there any feature I might be missing for a more robust solution?
Upvotes: 1
Views: 245
Reputation: 36491
To accomplish something like this, to facilitate communication to my app of my app's own URLs in a DRY way, to propagate query parameters debug settings used in development through internal links, and also because I find Play's reverse router to be super ugly, I created a URL abstraction I like a lot better. It works like this:
1) I created a type to represent URLs symbolically:
/** Represents a URL that can possibly be a webpage */
sealed trait PageUrl
case class SectionUrl(slug: String) extends PageUrl
case class StaticPageUrl(slug: String) extends PageUrl
case class ExternalUrl(url: String) extends PageUrl
2) I created a class for resolving these objects into full URLs:
/** Wrapper to propagate request override flags to internal links */
case class UrlWrapper(params: Seq[(String, String)]) {
def apply(url: PageUrl, additionalParams: Seq[(String, String)] = Seq.empty): String = {
url match {
case SectionUrl(slug) => urlAndParams(routes.PageRendererController.showSectionPage(slug).url)
case StaticPageUrl(slug) => urlAndParams(routes.PageRendererController.showStaticPage(slug).url)
case ExternalUrl(u) => u
}
}
def urlAndParams(url: String, additionalParams: Seq[(String, String)] = Seq.empty): String = {
def urlEncode = (u: String) => java.net.URLEncoder.encode(u, "UTF-8")
val formattedParams = (queryParams ++ additionalParams).map{ case (key, value) => s"$key=${urlEncode(value)}" }.mkString("&")
val paramOption = if (formattedParams.nonEmpty) Some(formattedParams) else None
(Seq(url) ++ paramOption).mkString(if (url.indexOf("?") > 0) "&" else "?")
}
}
You could easily modify this to provide a prefix always by default, or upon request via some other method.
3) In a trait/class that all my views extend, I declare an implicit field of type UrlWrapper
, that will be available to my templates, so I can do:
@(option1: String, urlParam: PageUrl)(implicit url: UrlWrapper)
...
<a href="@url(urlParam)">My link</a>
...
As a bonus, since my pages all correspond to models in my app, I added to UrlWrapper
additional methods for converting model objects to resolved URLs:
case class UrlWrapper(...) {
...
def forSection(section: Section, additionalParams: Seq[(String, String)] = Seq.empty): String = {
apply(SectionUrl(section.slug), additionalParams)
}
def forStaticPage(staticPage: StaticPage, additionalParams: Seq[(String, String)] = Seq.empty): String = {
apply(StaticPageUrl(staticPage.slug), additionalParams)
}
}
Upvotes: 1
Reputation: 14649
routes
to context.routes
routes
fileadd to routes
the line:
-> /context context.Routes
Upvotes: 3