tl;dr - I want to set a put-attribute in my tiles definitions based on the individual model passed to a view in tiles
I'm trying to create a page to display a user using spring and tiles. Right now my page gets the model fine, but I want the title of the page to include the username (which it would have to get from the model). I've included some excerpts from my code below:
tiles.xml:
<definition name="baseLayout" template="/WEB-INF/jsp/layout/layout.jsp">
<put-attribute name="title" value="FitterBlog" />
<put-attribute name="header" value="/WEB-INF/jsp/layout/header.jsp" />
<put-attribute name="nav" value="/WEB-INF/jsp/layout/nav.jsp" />
<put-attribute name="body" value="" />
<put-attribute name="footer" value="/WEB-INF/jsp/layout/footer.jsp" />
<put-attribute name="ads" value="/WEB-INF/jsp/layout/ads.jsp" />
<put-attribute name="css-layout" value="/FitterBlog/resources/css/layout.css" />
</definition>
<definition name="user/display" extends="baseLayout">
<put-attribute name="title" value="FitterBlog - ${user.username}" />
<put-attribute name="body" value="/WEB-INF/jsp/user/display.jsp" />
</definition>
As you can see, I've tried using the same syntax as I would in a jsp to display the username. ie. I tried using ${user.username} to display the username, however that doesn't work and I just get the text "${user.username}" displayed in the title instead of the actual username.
display.jsp:
//output the username from the user model, this works fine
${user.username}
UserController.java
#RequestMapping(value="display/**")
public ModelAndView displayUser(#ModelAttribute("user") User user, BindingResult result) {
//TODO
//retrieve user number from the URL
//retrieve user from database
//display user details
user.setUsername("Awesome username!");
return new ModelAndView("user/display", "user", user);
}
as you can see, I'm still in heavy development and currently set the username on the user object myself (instead of getting it from the database), but that's beside the point.
After reading a few different sites (also, thanks for the link Ralph, it gave me a place to start) I have found a solution.
Basically what I needed to do was edit my DTD to use version 2.1 and also to use an expression put-attribute instead of a value put-attribute. Here is the relevant part to the tiles.xml file:
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
"http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<definition name="user/view" extends="baseLayout">
<put-attribute name="title" expression="FitterBlog | ${user.username}" />
<put-attribute name="body" value="/WEB-INF/jsp/user/view.jsp" />
</definition>
See this part of the tiles documentation: Expression Language support
It looks like you need to enable the EL-Support first.
I know this is not a complete solution, but I hope this hint helps to resolve the problem. -- If it helps, please post the solution you found.
Related
I'd like to be able to define a base definition, where I can inherit a list of styles and scripts. then define a page definition that inherits base definition, and adds page specific styles and scripts. Is this possible -or am I not thinking about this in the right way? I would have thought this to be a fairly basic idea.
base definitions
<tiles-definitions>
<!-- base styles -->
<definition name="base.styles" >
<put-list-attribute name="styles" cascade="true" >
<add-attribute value="/view/common/jquery-ui-theme-base-v1.12.1.css" />
</put-list-attribute>
</definition>
<!-- base scripts -->
<definition name="base.scripts" >
<put-list-attribute name="scripts" cascade="true" >
<add-attribute value="https://code.jquery.com/jquery-3.1.0.min.js" />
<add-attribute value="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" />
</put-list-attribute>
</definition>
<!-- base definition -->
<definition name="base.definition" template="/WEB-INF/page/defaultLayout.jsp" >
<put-attribute name="title" />
<put-attribute name="styles" value="base.styles.styles" cascade="true" />
<put-attribute name="header" value="/WEB-INF/page/common/header.jsp" />
<put-attribute name="body" />
<put-attribute name="scripts" value="base.scripts.scripts" cascade="true" />
<put-attribute name="footer" value="/WEB-INF/page/common/footer.jsp" />
</definition>
</tiles-definitions>
note the values of the put-attribute match the names of the definitions above them. (my guess is this isn't correct)
page specific definitions
<tiles-definitions>
<!-- page specific styles -->
<definition name="samplePage.styles" extends="base.styles" >
<put-list-attribute name="styles" inherit="true" >
<add-attribute value="/view/page/samplePage/samplePageStyles.css" />
</put-list-attribute>
</definition>
<!-- page specific scripts -->
<definition name="samplePage.scripts" extends="base.scripts" >
<put-list-attribute name="scripts" inherit="true" >
<add-attribute value="/view/page/samplePage/samplePageScript.js" />
</put-list-attribute>
</definition>
<!-- page specific definition -->
<definition name="samplePage" extends="base.definition" >
<put-attribute name="title" value="Sample Page" />
<put-attribute name="styles" value="samplePage.styles" cascade="true" />
<put-attribute name="body" value="/WEB-INF/page/samplePage/samplePageBody" />
<put-attribute name="scripts" value="samplePage.scripts" cascade="true" />
</definition>
</tiles-definitions>
again -note the values of the put-attribute match the names of the definitions above them. (probably not correct?)
I'm currently getting an IllegalArgumentException
Caused by: java.lang.IllegalArgumentException: Cannot convert samplePage.styles of type class java.lang.String to class org.apache.tiles.Attribute
at com.sun.el.lang.ELSupport.coerceToType(ELSupport.java:428)
at com.sun.el.ExpressionFactoryImpl.coerceToType(ExpressionFactoryImpl.java:85)
... 104 more
It appears as if the put-attributes for the styles and scripts are not picking up the definitions of the same name above them... but I'm not sure what to do to correct it. Any ideas?
To answer the question how to reference another definition in put-attribute, this appears to be fairly easy with jsp components such as the following (note the definition, and the 'type' attribute):
<definition name="default.header" template="/WEB-INF/page/common/header.jsp" />
... then elsewhere...
<put-attribute name="header" type="definition" value="default.header" />
However, it doesn't look like this can be done with resources such as stylesheets and scripts, as they don't translate back to an actual template. So, I reworked my base-definition as follows, which still allows me to have 'global' resources, as well as 'page-specific' resources. Also allows me to write the jsp components once, define them once, and reference them many times.
common definitions (tiles.common.xml)
<tiles-definitions>
<definition name="default.header" template="/WEB-INF/page/common/header.jsp" />
<definition name="default.footer" template="/WEB-INF/page/common/footer.jsp" />
[... all the other common components...]
</tiles-definitions>
base definition (tiles.base.xml)
<tiles-definitions>
<definition name="base.definition" template="/WEB-INF/page/baseLayout.jsp" >
<put-attribute name="title" type="string" />
<put-attribute name="header" type="definition" value="default.header" />
<put-attribute name="body" />
<put-attribute name="footer" type="definition" value="default.footer" />
<put-list-attribute name="styles" inherit="true" >
<add-attribute value="/view/common/jquery-ui-theme-base-v1.12.1.css" />
[... other styles common to all pages...]
</put-list-attribute>
<put-list-attribute name="scripts" inherit="true" >
<add-attribute value="https://code.jquery.com/jquery-3.1.0.min.js" />
<add-attribute value="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" />
[... other scripts common to all pages...]
</put-list-attribute>
</definition>
</tiles-definition>
page specific definition (tiles.samplePage.xml)
<tiles-definitions>
<definition name="samplePage" extends="base.definition" >
<put-attribute name="title" value="Sample Page" />
<put-attribute name="body" value="/WEB-INF/page/samplePage.jsp" />
<put-list-attribute name="styles" inherit="true" >
<add-attribute value="/view/pages/samplePage.css" />
[... other page specific styles ...]
</put-list-attribute>
<put-list-attribute name="scripts" inherit="true" >
<add-attribute value="/view/pages/samplePage.js" />
[... other page specific scripts ...]
</put-list-attribute>
</definition>
</tiles-definition>
I see PLACEHOLDER on result page instead of edit.jsp page content. Source of tiles-defs.xml listed below.
<definition name="layout" template="/WEB-INF/tiles/layout.jsp">
<put-attribute name="body">
<definition template="/WEB-INF/tiles/user/layout.jsp">
<put-attribute name="userMenu" value="/WEB-INF/tiles/user/menu.jsp"/>
<put-attribute name="userContent">
<definition template="/WEB-INF/tiles/user/profile/layout.jsp">
<put-attribute name="profileNav" value="/WEB-INF/tiles/user/profile/nav.jsp"/>
<put-attribute name="profileContent" value="PLACEHOLDER"/>
</definition>
</put-attribute>
</definition>
</put-attribute>
...
</definition>
<definition name="editUser" extends="layout">
<put-attribute name="profileContent" value="/WEB-INF/jsp/user/auth/edit.jsp"/>
...
</definition>
Also tried to split in 3 separate definitions without any result. Also played with cascade attribute without any result too.
Pls. add to top of /WEB-INF/tiles/user/profile/layout.jsp:
<%#taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
I want to have some nested templates with tiles, but without any luck for the moment.
My main template :
[...]
<div id=body><tiles:insertAttribute name="body"/></div>
[...]
My nested template:
[...]
<div id=sub><tiles:insertAttribute name="sub"/></div>
[...]
My view definition:
<tiles-definitions>
<definition name="mainTemplate" template="/mainTemplate.jspx">
</definition>
<definition extends="mainTemplate" name="subTemplate">
<put-attribute name="body" value="/subTemplate.jspx"/>
</definition>
<definition extends="mainTemplate" name="myView">
<put-attribute name="sub" value="blablabla"/>
</definition>
</tiles-definitions>
What I expected :
<div id=body><div id=sub>blablabla</div></div>
What I got :
<div id=body><div id=sub></div></div>
I have seen the documentation on the tiles website about this subject but I can't make it work :(
Actually, "subTemplate" and "myView" are two different definitions extending "mainTemplate" both. From what I understand you want the"myView" tile to be nested inside the "subTemplate" tile.
You could try something like this :
<tiles-definitions>
<definition name="mainTemplate" template="/mainTemplate.jspx">
<definition name="subTemplate" extends="mainTemplate">
<put-attribute name="body">
<definition template="/subTemplate.jspx">
<put-attribute name="myView" value="blablabla" />
</definition>
</put-attribute>
</definition>
</tiles-definition>
I'm using Spring MVC 3 + Apache Tile 2.2 and I was just wondering if I could use properties directly from tiles-def.xml file. So my tiles-def looks like:
<definition name=".mainTemplate" extends=".client1MainTemplate">
<put-attribute name="title" value="Title1" type="string" />
</definition>
and I'd like to put the value of the Title into the messages.properties file instead of putting it here. Any way of doing it?
Cheers
I would do something like this:
<definition name="*" extends=".mainTemplate">
<put-attribute name="viewName" value="{1}"/>
<put-attribute name="body" value="/WEB-INF/views/{1}.jsp" />
</definition>
and in your template file:
<tiles:importAttribute name="viewName"/>
<title><spring:message code="${viewName}.title"/></title>
For a view name "index", this would look for "index.title" in messages.properties.
I have the following tiles-def.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
<definition name="default" template="/WEB-INF/jsp/layout.jsp">
<put-attribute name="title" value="my webapp" />
<put-attribute name="header" value="/WEB-INF/jsp/header.jsp" />
<put-attribute name="menu" value="/WEB-INF/jsp/menu.jsp" />
<put-attribute name="body" value="/WEB-INF/jsp/hello.jsp" />
<put-attribute name="footer" value="/WEB-INF/jsp/footer.jsp" />
</definition>
<definition name="contact" extends="default">
<put-attribute name="body" value="/WEB-INF/jsp/contact.jsp" />
</definition>
<definition name="hello" extends="default">
<put-attribute name="body" value="/WEB-INF/jsp/hello.jsp" />
</definition>
</tiles-definitions>
As you can see, in all my pages, just the body changes. Is there a way to automate this? (so it automatically includes the jsp that has the name of the requested page, or an error page if it doesnt exist?)
Thank you!
EDIT: More details of what I want to do:
How it is now:
For each new page of my webapp, I have to add a new to my tiles-def.xml. And everytime, the only thing it does is setting the body with my content page.
What I would like to do:
Just have one default definition that automatically chose the page to display, something like this:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
<definition name="default" template="/WEB-INF/jsp/layout.jsp">
<put-attribute name="title" value="my webapp" />
<put-attribute name="header" value="/WEB-INF/jsp/header.jsp" />
<put-attribute name="menu" value="/WEB-INF/jsp/menu.jsp" />
if(myPageName exists){
<put-attribute name="body" value="/WEB-INF/jsp/" + myPageName + ".jsp" />
else{
<put-attribute name="body" value="/WEB-INF/jsp/error404.jsp" />
}
<put-attribute name="footer" value="/WEB-INF/jsp/footer.jsp" />
</definition>
</tiles-definitions>
Where myPageName would be the name of the page requested (If the URL was "http://www.mysite.com/contact.html", then myPageName would be "contact". If it was "http://www.mysite.com/info.html", then myPageName would be "info").
How can I do this?
Check out the Dynamic Tiles Example on the springbyexample.org page. The DynamicTilesView does exactly what you want (see example config here). The full Dynamic Tiles Example can be found here on github