Nested Thymeleaf Templates in Spring - java

Short version
How is one supposed to make nested templates in Thymeleaf when using Spring? It appears asterisk notation is not supported ("*{mailingAddress}") inside th:object attributes in Spring. Is there a work-around / different tag to use?
Long version
For example, let's say I have these classes:
class Address { String street; }
class Person { Address mailingAddress; Address shippingAddress; }
class Order { int orderNo; Person customer; }
So I make an address.html Thymeleaf template:
<span th:text="*{street}"></span>
We test it with a sample Address. Looks good.
and I make a person.html Thymeleaf template that references the address like so:
<span th:text="*{firstName}"></span>
<span th:object="${person.shippingAddress}">
<span th:include="fragments/address :: address"></span>
</span>
And we test it with an example person. I could even reference the same template and set the context to be the ${person.mailingAddress}. So far so good.
Now let's make our Order template. Only, hey, wait. Earlier, in our person.html file we said ${person.shippingAddress} but now we need it to say ${order.customer.shippingAddress}. If I were not using Spring I'd put the following into person.html:
<span th:text="*{firstName}"></span>
<span th:object="*{shippingAddress}">
<span th:include="fragments/address :: address"></span>
</span>
That way, no matter what my path to getting here all I have to care about is that my current context has a shippingAddress. I could then use person.html directly as well as within my order.html template.
Unfortunately I am in Spring, so I get the following exception:
org.thymeleaf.exceptions.TemplateProcessingException:
The expression used for object selection is *{shippingAddress},
which is not valid: only variable expressions (${...}) are
allowed in 'th:object' attributes in Spring-enabled environments.
(include:510)
at org.thymeleaf.spring4.processor.attr.SpringObjectAttrProcessor.validateSelectionValue(SpringObjectAttrProcessor.java:73)
at org.thymeleaf.standard.processor.attr.AbstractStandardSelectionAttrProcessor.getNewSelectionTarget(AbstractStandardSelectionAttrProcessor.java:69)
at org.thymeleaf.processor.attr.AbstractSelectionTargetAttrProcessor.processAttribute(AbstractSelectionTargetAttrProcessor.java:61)
To move forward I must duplicate all my nested templates. In this example, I would have one person.html with th:object="${person.mailingAddress}" calling to address.html, and a duplicate of person.html called orderCustomer.html where we change the line to th:object="${order.customer.mailingAddress}", but is otherwise identical.
Is there a work-around out there that would let me re-use templates?

You can report a bug to the thymeleaf developers in github, or fork the project to add this functionality and convince the Daniel Fernández to accept it.
https://github.com/thymeleaf/thymeleaf/issues
Or else, he is available in StackOverflow. You can simply send him a message about the posibility of integrating this functionality
https://stackoverflow.com/users/550664/daniel-fern%C3%A1ndez
apart from that there is nothing much we can do rather to stick with the approach of putting th:object="${person.mailingAddress}" and th:object="${order.customer.mailingAddress}" outside each import.

Related

Spring boot, #field is

I get this error when I use "${#fields.hasErrors('whatever')}"
The identifier [#fields] is not a valid Java identifier as required by section 1.19 of the EL specification (Identifier ::= Java language identifier). This check can be disabled by setting the system property org.apache.el.parser.SKIP_IDENTIFIER_CHECK to true.
How can I fix this?
looking at the official thymeleaf documentation, we see this example:
<input type="text" th:field="*{datePlanted}" />
<p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p>
looking at that example, it seems like you need to put a thymeleaf field (which you passed from your controller and/or get it from a th:object) into the hasErrors bracket but in single quotation marks.
if you already did that, maybe check if you have thymeleaf imported in your html template (i doubt that you forgot that but may still wanna check that) and if thats the case, let me know.

Dynamically creating a menu of available scaffoldings in Grails

I have a bunch of model classes in my Grails app, all of which are for the purpose of dynamic scaffolding of their respective database tables. For the index of the app, I'd like to have a menu of all these scaffoldings so that if a new model is added, the menu is updated.
Is there any automatic, Grails way of doing this or am I stuck with creating a naive index view with a bunch of g:link's statically typed out for each class to take the user to their respective CRUD views?
As an extension to Joshua's answer, the following should get you a list of scaffolded controllers, in grails 3 at least.
<g:each var="c" in="${grailsApplication.controllerClasses.findAll{ it.isReadableProperty('scaffold') } }">
<li><g:link controller="${c.logicalPropertyName}">${c.fullName}</g:link></li>
</g:each>
EDIT
As requested in the comments, to get the table name you'll need access to the sessionFactory which you'll need to inject into a controller, something like the following will get you a map of domain name to domain table name.
Controller
class YourController {
def sessionFactory
def index() {
def scaffoldedTables = grailsApplication.controllerClasses.findAll{ it.isReadableProperty( 'scaffold' ) }.collectEntries {
[(it.name): sessionFactory.getClassMetadata(it.getPropertyValue( 'scaffold', Object.class )).tableName]
}
[scaffoldedTables: scaffoldedTables]
}
}
gsp
<g:each var="c" in="${scaffoldedTables}">
<li><g:link controller="${c.key}">${c.value}</g:link></li>
</g:each>
You could just simply build a list of them like this:
<ul>
<g:each var="c" in="${grailsApplication.controllerClasses.sort { it.fullName } }">
<li><g:link controller="${c.logicalPropertyName}">${c.fullName}</g:link></li>
</g:each>
</ul>
This code was taken from an old Grails 1.3x project from the default index.gsp. Not sure if it still works in recent versions of Grails, but it should at least give you an idea of what you can do that would be dynamic.
UPDATE
As Jeff Scott Brown has pointed out this will include ALL controllers, scaffolded or not, and those provided by plugins as well. In theory you could further filter the resulting classes from grailsApplication.controllerClasses, inspecting them for their package (assuming your domain is in a known package or packages) and/or if they are scaffolded (static scaffold = true).

WebObjects: Using WOConditional inside <div>

I am in a situation where the "class" attribute of a div tag should be dependent on the value of a java binding. This can be easily done by moving the associated logic to the java class, but at this moment we are not allowed to change anything at the Java component.
I am trying out the following to resolve the problem (using WOOGNL):
<div class="<wo:WOConditional condition = \"[cssClassDecider]\">classToUse</wo:WOConditiona>" >
HTML Static Content
</div>
As it can be seen, i am trying to use value of "cssClassDecider" to set the class.
Can anybody tell if any has solved a similar problem or one is available at WO.
It's not clear to me whether cssClassDecider is providing the string content for the class attribute, or a boolean to drive a conditional. In any case, the usual pattern would be:
<wo:WOGenericContainer elementName="div" class="$methodReturningClassNames">
...
</wo:WOGenericContainer>
If cssClassDecider returns a conditional, you could do something like this:
<wo:WOConditional condition="$cssClassDecider">
<div class="classWhenTrue">
...
</div>
</wo:WOConditional>
<wo:WOConditional condition="$cssClassDecider" negate="$true">
<div class="classWhenFalse">
...
</div>
</wo:WOConditional>
If neither of those solve your problem, provide some more information.

Understanding the concept of data binding in a Spring-MVC app

I'm trying to understand the concept of data binding in Spring-MVC with Velocity (I'm learning this framework and porting an app to this platform).
I'm used to getting form variables using request.getParameter("username"), in the Spring world it seems that I can perform validation and such against "form objects" e.g. a datamodel style object that represent all the fields of a form.
The concept of a validator makes sense, but marshaling the data from a query string to these objects is fuzzy for me still. This is the concept of "Data Binding" correct?
If I'm correct to this point a few specific questions:
When a "binding" is made between a form variable (say "username" for example) and the the field of an object (say org.a.b.MyNewUserFormObj.username) is that "binding" a permanent definition such that all subsequent http posts of that form cause the username form variable to be assigned to org.a.b.MyNewUserFormObj.username?
How in the world do I accomplish the above binding definition? (if what I've said up to now is correct I feel like Costello in 'Who's on First', I don't even know what I just said!), I just need a conceptual picture.
Thanks for setting straight a brain gone astray.
There is no magic in data binding.
Actually, Spring simply populate properties of #ModelAttribute object with the values of request parameters with the corresponding names (in the simpliest case request parameter have the same name as a property, but nested properties are also supported).
So, if you have
<input type = "text" name = "firstName" />
and
public class Person {
private String firstName;
... getters, setters ...
}
you get a value from the form field.
Spring also provides convenient method for creating HTML forms. So, instead of creating form fields manually, you can write in JSP:
<form:form modelAttribute = "person" ...>
<form:input path = "firstName" />
</form:form>
or in Velocity (note that in this case <form> is created manually and property path is prefixed with the model attribute name):
<form ...>
#springFormInput("person.firstName" "")
</form>
Fields of the forms generated this way will be prepopulated with the values of the corresponding properties of the model attribute (that's why model attribute name is needed).

Using HTML builders in grails instead of GSP

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.

Categories