I have an interesting problem. I'm trying to integrate the Rythm template engine into my Spring MVC application, so I wrote a simple view resolver that invokes RythmeEngine to render files. However, when I try to use #extends(...), the engine returns a blank render. I have two template files:
layout.html
<!DOCTYPE html>
<html>
<head>
<title>Rythm Sandbox</title>
</head>
<body>
<h1>Welcome!</h1>
#render()
</body>
</html>
rhythm.html
#extends(layout)
Hello World!
I am invoking RythmEngine as follows:
RythmEngine eng = RythmEngineFactory.getEngine();
File f = new File(someRootPath + name + ctx.getSuffix());
resp.setContentType("text/html");
eng.render(httpResponse.getWriter(), f, model);
And RythmEngineFactory.getEngine() returns a singleton RythmEngine with the following configuration:
Map<String, Object> props = new HashMap<String, Object>();
props.put("engine.mode", "dev");
props.put("home.template.dir", myTemplateRootDirectoryPath);
return new RythmEngine(props);
Problem
Here's the problem: without the #extends(layout) line in rhythm.html, I get a render "Hello World" as expected. However, when I try to extend layout.html, it results in a blank render (i.e., no output at all). I know it's finding the layout.html file, because if I replace #extends(layout) with something like #extends(layoutGooBlah), the error message says "Cannot find extended template by name 'layoutGooBlah'". So I know it's finding my template fine, but why would the output be blank??
Edit:
Interesting phenomenon: when I change this line:
eng.render(httpResponse.getWriter(), f, model);
To:
String s = eng.render(f, model);
httpResponse.getWriter().write(s);
It works. ??? Why won't it write to a PrintStream when using #extends??
The API engine.render(writer, ...) is buggy. See https://github.com/greenlaw110/Rythm/issues/201
BTW, I recommend you use http://github.com/greenlaw110/spring-rythm for your spring MVC application
Related
So I'm trying to just follow instructions for simple Spring Boot project using devtools+mustache+data-jpa. I'm just copy-pasting the whole thing and it doesn't work, even thought tutorial says "Just press the button and it works". Full source code is here, some listings I will provide in the end.
All I want to do is to redirect to index.html from localhost:8080/ and insert simple value into the template.
But instead:
1. Something redirects me from / to /apex/f?p=4950:1 for some reason
2. If I change mapping to #GetMapping("/home") and try localhost:8080/home I get 404
After enabling logging I found out that PathResourceResolver doesn't scan the /resources/templates directory. And if I add dependency on Thymeleaf, it finds it.
So the question is where is the problem? Should I add some config file? Or Mustache isn't working like that?
IndexController.java
#Controller
public class IndexController {
#GetMapping("/")
public ModelAndView home() {
Map<String, String> model = new HashMap<>();
model.put( "name", "Alex" );
return new ModelAndView( "index", model );
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Welcome to Spring, {{ name }}</h1>
</body>
</html>
Dependencies
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-mustache')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('org.springframework.boot:spring-boot-devtools')
runtime('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
Structure
Log
In order for given demo app to work, please add following to the main/resources/application.properties
spring.mustache.prefix=classpath:/templates/
spring.mustache.suffix=.html
This will tell Spring where to look for Mustache views and what extension those are supposed to have.
final Context ctx = new Context();
context.setVariable("data", data);
templateEngine.process(template, context).trim();
here are the imports:
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
template variable points to "content_completed". Where this content_completed is a html file that exists in the project classpath.
contents of this html file:
<html xmlns:th="http://www.thymeleaf.org" th:inline="text" th:remove="tag">
[[${data.fileName}]][[${T(abc.composer.NoteData).COMPLETED_NO_ERRORS}]]
</html>
where NoteData is a java class
If this data.fileName has something like "sample&.text", the Thymeleaf template engine is changing it to "sample&.text".
Any thoughts on how to avoid this?
Solved it myself. Here is the solution:
<html xmlns:th="http://www.thymeleaf.org" th:inline="text" th:remove="tag">
<th:block th:utext="${data.fileName}"/>[[${T(abc.composer.NoteData).COMPLETED_NO_ERRORS}]]
</html>
Thymeleaf is escaping the filename so that a browser does not interpret the & as a special HTML character (it's used for HTML entities).
So, the answer, I think, is that it's doing the right thing. When you view the HTML output in a browser it will render as you are expecting.
I am working on a functionality where the application needs to generate user specific emails. This will be setup or configured on the user level using a email template which essentially contains a SQL query, column model, data type, subject, header, footer etc. The template serves as the dataset and layout for the email.
Now using this XML template I need to generate the HTML email. The application will read the XML, execute the SQL query and then match the resultset to the column model. Beyond this; is there any framework or API that can help generate the HTML response (nicely formatted css table) from Java objects or it has to be cooked using raw HTML tags (, etc.)?
I was also researching to see if BIRT or Jasper can provide HTML response but it doesn't seem like they are meant for that. If anyone has experience building a solution for such a use case please let me know.
Take a look at Thymeleaf. It's a HTML template engine.
It's as simple as this:
ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setTemplateMode("HTML5");
resolver.setSuffix(".html");
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(resolver);
final Context context = new Context(Locale.CANADA);
String name = "John Doe";
context.setVariable("name", name);
// add more objects from your ResultSet
final String html = templateEngine.process("myhtml", context);
with a myhtml.html file:
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-3.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>My first template with Thymeleaf</title>
</head>
<body>
<p th:text="${name}">A Random Name</p>
</body>
</html>
Here the placeholder ${name} will replace the value A Random Name in the <p> element by the value you inserted in the context.
As for your requirement of reading and generating a table, Thymeleaf provides constructs to loop as many times as is required (ie. as long as you have data remaining). Example:
<tr th:each="prod : ${allProducts}">
will iterate through allProducts, assigning each object to the variable prod at each iteration. Take a look at the tutorials and the docs for more.
Notice, you have to write the HTML yourself.
Take a look at this answer for generating HTML report through Jasper
You can use XSLT to transform your XML to HTML. The result of your SQL query would have to be inserted as XML beforehand.
I'm trying to call render from a controller for a tag (function) inside a template instead of the template. This way I could use it for partial renderings of a page from ajax calls. Of course I could separate the components of the form in several templates an call render on those but I think it would be cleaner the other way.
What I was trying to do is like the following:
formpage.scala.htm
#()
<html>
...
#content
...
</html>
#**********************************
* Helper generating form *
***********************************#
#content() = {
<h3 class="form-heading">#Messages("employees")</h3>
#form(routes.AppController.save()) {
#inputText...
...
}
And using ajax render the content function, without having to separate it to a
separate file. This way I could render portions of the template without fragmenting it
in multiple files.
De facto the tag is just smaller template, so you can use tags for both - using in templates and controllers, the simplest sample:
/app/views/tags/mytag.scala.html
This is my tag...
In controller can be rendered as:
public static Result createFromTag(){
return ok(views.html.tags.mytag.render());
}
In other template you just to insert:
....
And there is my tag rendered
<b>#tags.mytag()</b>
More flexibility
Of course as it's template ergo Scala function as well you can just pass some params to it or even Html body:
/app/views/tags/othertag.scala.html
#(headline: String)(body: Html)
<h3>#headline</h3>
<div class="tagsBody">
#body
</div>
In controller can be rendered as:
public static Result createFromTag(){
return ok(
views.html.tags.othertag.render(
"Head from controller",
new play.api.templates.Html("This code becomes from <i>controller</b>")
)
);
}
(of course you can import these two for shorter code in action import play.api.templates.Html; and import views.html.tags.othertag)
Finally in your template you can use the tag as:
And there is my tag rendered <br/>
#tags.othertag("Head from template"){
some content for the tag's body from <b>The Template!</b>
}
final.
You'll find tags description in documentation.
is there a way to use groovy builders to build JSP files in a Grails application keeping things enough integrated?
To explain better: by default Grails uses gsp files that are nice but quite verbose..
<div class="clear">
<ul id="nav">
<li><g:link controller="snippets" action="list">Snippets</g:link></li>
<li><g:link controller="users" action="list">Users</g:link></li>
<li><g:link controller="problems" action="list">Problems</g:link></li>
<li><g:link controller="messages" action="list">Messages</g:link></li>
</div>
<div id="content">
is there a way to use groovy.xml.MarkupBuilder tha would turn the previous piece into
div(class:'clear') {
ul(id:'nav') {
li { g_link(controller:'snippets', action:'list', 'Snippets') }
// and so on
Of course g_link is invented just to give the idea..
Do a search for builder under the web layer section of the grails user guide. There is an example in there that shows you exactly how to do this using the xml builder.
I don't have a complete answer for you, but I suspect the key will be gaining access to the "view resolvers". In a normal SpringMVC app, these are configured in views.properties (or views.xml) as follows:
csv=com.example.MyCSVResolver
xml=com.example.MyXMLResolver
audio=com.example.MySpeechResolver
In a regular SpringMVC app, you return something like new ModelAndView(myModel, 'csv') from a controller action.
This would cause the CSVResolver class to be invoked passing it the data in myModel. In addition to containing the data to be rendered, myModel would likely also contain some formatting options (e.g. column widths).
Spring searches the views file for a key matching the view name. If it doesn't find a match, by default it just renders a JSP with the view name and passes it the model data.
Now back to Grails....remember that Grails is really just a Groovy API over SpringMVC and most of the features of SpringMVC can be accessed from Grails. So if you can figure out how to modify the views file, just change your controller actions to return an appropriate ModelAndView instance, and it should work as described above.
GSP allows you to run arbitrary Groovy code inside <% %> brackets. So you can have something like this (borrowing example from page linked to by BlackTiger):
<% StringWriter w = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(w)
builder.html{
head{
title 'Log in'
}
body{
h1 'Hello'
builder.form{ }
}
}
out << w.toString()
%>
Note that the above calls g:form tag, and you can pass additional stuff to it.
So what you are asking for is certainly possible, though I am not sure if it will end up being a win. I'd suggest you perhaps look more at TagLibs in combination with Templates and SiteMesh Layouts - can definitely simplify things tremendously.