Comments (9)
Let's start with the simpler case: @footer()
. My guess at why you're getting a "not found" error is because you are missing the import. The .scala.html
templates get compiled into the view.html
package, so other classes in that package see them automatically. However, .scala.stream
templates get compiled into the view.stream
package, so you'd need to explicitly import the view.html
package.
Try the following in a streaming template:
@import views.html.tags._
@footer()
from ping-play.
The import works for the "not found" issue.
however when I just use @footer() I get the content escaped
<!-- FOOTER -->
<footer>
<!-- footer content -->
<div class="footer-content">
instead of
<!-- FOOTER --> <footer> <!-- footer content --> <div class="footer-content">
I can use
@HtmlStream.fromHtml(footer())
which is better. I also try an implicit converter from Html to HtmlStream without success
@implicitHtmlToStream(html:Html) = @{
HtmlStream.fromHtml(html)
}
// this works
@implicitHtmlToStream(footer())
// this doesn't work
@footer()
from ping-play.
OK, glad to hear the import thing works. You may want to add those imports to TwirlKeys.templateImports
in your build.sbt
file.
The escaping issue is something that would be good to fix. I suspect your approach with implicits should work. Let me experiment a bit.
from ping-play.
Ah, actually, Scala won't bother to look for an implicit in this case. That's because the template gets compiled into a series of calls to a _display_
method that takes Any
as an argument and returns an HtmlStream
. We need some other way to inject this behavior...
from ping-play.
Unfortunately, I don't see any way to fix this without a change to Play's Twirl template compiler. The core of the issue is that templates get compiled into a sequence of calls to the _display_
method in BaseScalaTemplate:
def _display_(o: Any)(implicit m: Manifest[T]): T = {
o match {
case escaped if escaped != null && escaped.getClass == m.runtimeClass => escaped.asInstanceOf[T]
case () => format.empty
case None => format.empty
case Some(v) => _display_(v)
case xml: scala.xml.NodeSeq => format.raw(xml.toString())
case escapeds: immutable.Seq[_] => format.fill(escapeds.map(_display_))
case escapeds: TraversableOnce[_] => format.fill(escapeds.map(_display_).to[immutable.Seq])
case escapeds: Array[_] => format.fill(escapeds.view.map(_display_).to[immutable.Seq])
case string: String => format.escape(string)
case v if v != null => format.escape(v.toString)
case _ => format.empty
}
}
If the type of the item in your template is HtmlStream
, it gets passed through unchanged. If it's Html
, it skips down to the before-last case statement and calls format.escape(v.toString)
on it, which is what leads to your HTML being escaped.
I can't find a way to modify this behavior. Implicits won't do it because the compiler has no reason to look for them. I can't override the _display_
method because I don't directly control the class that gets generated for my template. The twirl compiler handles that and, as far as I can see, offers no way to override methods or specify the base class.
The closest I've been able to come is to override _display_
directly in the body of my template:
@_display_(x: Any) = @{
x match {
case html: Html => HtmlStream.fromHtml(html)
case _ => super._display_(x)
}
}
@footer() // This will no longer be escaped
Defining this at the top of every template is a chore, and you can't import such a method from another class, or you get a "reference to display is ambiguous" error.
If anyone has any suggestions, please let me know. In the meantime, the workaround is to wrap calls to .scala.html
templates (e.g. @footer()
) with HtmlStream.fromHtml()
(e.g. @HtmlStream.fromHtml(footer())
).
from ping-play.
Thanks for the quick answer. It looks like we can't do really better than @HtmlStream.fromHtml(footer()) so I'll stick we it
I try to reuse an html template (aka menu/footer) in a stream page and can't figure it out.
//detail.scala.stream
@(bigPipe : BigPipe
, enterprise: Pagelet
, investors : Pagelet
, comments : Pagelet
, documents : Pagelet
)(implicit request: RequestHeader, lang:Lang)
@main("meta.application.index.title"
, "meta.application.index.description"
, "meta.application.index.keyword") {
<h1>Coucou</h1>
//I try also @Html("<h1>coucou</h1>") without success
}
//main.scala.html
@(title : String
, description: String = ""
, keywords : String = ""
, scripts : Html = Html("")
, styles : Html = Html("")
)(content: Html)(implicit request: RequestHeader, lang:Lang)
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Title</title>
</head>
<body>
@content
</body>
</html>
I always get
type mismatch; found : com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamFormat.Appendable (which expands to) com.ybrikman.ping.scalaapi.bigpipe.HtmlStream required: play.twirl.api.Html
from ping-play.
In a .scala.stream
template, all markup will be wrapped in an HtmlStream
. For example, the following template:
@main(...) {
<div>Some markup</div>
}
Gets roughly compiled to:
main(...)(format.raw("""<div>Some markup</div>"""))
The format
is defined by the template type, and in this case, it's HtmlStreamFormat
, who's raw
method returns an HtmlStream
. Since your main
template is looking for Html
, this leads to a compile error.
There are two possibilities:
- The content you want to pass into the
main
template contains streaming content (e.g., pagelets). In this case, there is no way to reuse themain
template directly, since it returnsHtml
and the only way to use streaming is to returnHtmlStream
. You'll have to create a new streamingmain
template. If you want to reuse parts of the non-streaming one, break those into separate helper templates such ashead.scala.html
,nav.scala.html
,footer.scala.html
, etc (this is a good practice anyway). - The content you want to pass into the
main
template is completely static HTML. In that case, put it into a separate.scala.html
template and pass it tomain
:
<!-- staticMarkupForMain.scala.html -->
<div>Some markup</div>
<!-- index.scala.html -->
@main(...)(staticMarkupForMain())
from ping-play.
Thanks for your response. I think I understand way better how template works now
I try to reverse the process : define template in stream format and use it from html page
@content() = {
<h1>coucou</h1>
}
@views.stream.main("...")(HtmlStream.fromHtml(content()))
But it only display a toString version of the HtmlStream
com.ybrikman.ping.scalaapi.bigpipe.HtmlStream@718e5a8a
With the complexity of mixing 2 types of templates, is going full stream template a good option ? Do you see any drawbacks ?
The first I can see is when using external libs (e.g. play form helper) we need to wrap it.
from ping-play.
A .scala.stream
tempate will return an HtmlStream
object, which is a wrapper for an Enumerator[Html]
. There is no way to include those inside of a .scala.html
template, since those return an Html
object, which is a wrapper for a plain old String
.
In other words, if the base template you're rendering is a .scala.stream
template, you can include .scala.html
templates within it by wrapping them in an HtmlStream.fromHtml(...)
call:
// Controller
def index = Action {
Ok.chunked(views.stream.main(...))
}
<!-- main.scala.stream -->
<div>
<!-- Including other streaming templates is easy: -->
@someStreamingTemplate()
<!-- Including HTML templates requires a little wrapping: -->
@HtmlStream.fromHtml(someHtmlTemplate())
</div>
If the base template you're rendering is a .scala.html
template, you can only include other .scala.html
templates within it:
// Controller
def index = Action {
Ok(views.html.main(...))
}
<!-- main.scala.html -->
<div>
<!-- You CANNOT include a streaming template -->
<!-- This won't work: @someStreamingTemplate() -->
<!-- Including other HTML templates is easy: -->
someHtmlTemplate()
</div>
Therefore, it's more flexible to use .scala.stream
as the base template in your app. I would still build static HTML blocks using .scala.html
templates, as it communicates the intent better and makes them more reusable elsewhere (e.g. in tests). And you can reuse static HTML templates from external libs by wrapping them in HtmlStream.fromHtml(...)
calls.
from ping-play.
Related Issues (20)
- Invalid chunked response (play 2.3) HOT 3
- Update to Play 2.4.x with InjectedRoutesGenerator HOT 1
- Java examples no longer work HOT 3
- Docker image fails to build HOT 1
- Extension with HTMLCompressorFilter and GzipFilter HOT 3
- Killing open, not yet retuned Promises at the end of Controller. HOT 2
- Publish artifacts automatically as part of the build instead of manually
- Finish the "Composable Pagelets" implementation and documentation HOT 5
- Add support for pagelet priorities
- Add support for only rendering content that is visible
- Add support for specifying pagelet dependencies
- Add monitoring hooks
- Turn the sample apps into Typesafe Activator templates HOT 2
- Escaping not working HOT 9
- install ping-play HOT 5
- get /sample-app-common/src/main/scala/data in Java ? HOT 18
- upgrade to play 2.5 HOT 4
- Library is not compatible with Play 2.3-RC1 HOT 1
- Throwing runtime exceptions in case of chunked mode doesn't work HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ping-play.