[jboss-cvs] jboss-seam/doc/reference/en/modules ...
Gavin King
gavin.king at jboss.com
Tue Oct 24 10:28:18 EDT 2006
User: gavin
Date: 06/10/24 10:28:18
Modified: doc/reference/en/modules elenhancements.xml tutorial.xml
Log:
update blog tutorial
Revision Changes Path
1.3 +7 -1 jboss-seam/doc/reference/en/modules/elenhancements.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: elenhancements.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/elenhancements.xml,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- elenhancements.xml 24 Oct 2006 13:09:23 -0000 1.2
+++ elenhancements.xml 24 Oct 2006 14:28:18 -0000 1.3
@@ -42,7 +42,13 @@
</para>
<para>
- You may pass literal strings using single or double quotes:
+ Any value expression may be used as a parameter:
+ </para>
+
+ <programlisting><![CDATA[<h:commandButton action="#{hotelBooking.bookHotel(hotel.id, user.username)}" value="Book Hotel"/>]]></programlisting>
+
+ <para>
+ You may even pass literal strings using single or double quotes:
</para>
<programlisting><![CDATA[<h:commandLink action=â#{printer.println( âHello world!â )}â value=âHelloâ/>]]></programlisting>
1.69 +145 -95 jboss-seam/doc/reference/en/modules/tutorial.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: tutorial.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/tutorial.xml,v
retrieving revision 1.68
retrieving revision 1.69
diff -u -b -r1.68 -r1.69
--- tutorial.xml 22 Oct 2006 11:20:11 -0000 1.68
+++ tutorial.xml 24 Oct 2006 14:28:18 -0000 1.69
@@ -2840,7 +2840,7 @@
<literal><h:dataTable></literal> actually get initialized? Well, what
happens is that the <literal>Blog</literal> is retrieved
lazily—"pulled"—when needed, by a Seam component named
- <literal>blog</literal>. This is the opposite flow of control that is
+ <literal>blog</literal>. This is the opposite flow of control to what is
usual in traditional web action-based frameworks like Struts.
</para>
@@ -2901,92 +2901,75 @@
<para>
The blog example has a tiny form in the top right of each page that
- allows the user to search for blog entries. This is defined in the
- facelets template, <literal>template.xhtml</literal>:
+ allows the user to search for blog entries. This is defined in a
+ file, <literal>menu.xhtml</literal>, included by the facelets
+ template, <literal>template.xhtml</literal>:
</para>
<example>
<programlisting><![CDATA[<div id="search">
<h:form>
<h:inputText value="#{searchAction.searchPattern}"/>
- <h:commandButton value="Search" action="#{searchAction.search}"/>
+ <h:commandButton value="Search" action="/search.xhtml"/>
</h:form>
</div>]]></programlisting>
</example>
<para>
To implement a bookmarkable search results page, we need to perform a browser redirect
- after processing the search form submission. Seam provides a built-in component
- named <literal>redirect</literal> that makes it very easy to perform redirects with
- request parameters.
+ after processing the search form submission. Because we used the JSF view id as the
+ action outcome, Seam automatically redirects to the view id when the form is submitted.
+ Alternatively, we could have defined a navigation rule like this:
</para>
+ <example>
+ <programlisting><![CDATA[<navigation-rule>
+ <navigation-case>
+ <from-outcome>searchResults</from-outcome>
+ <to-view-id>/search.xhtml</to-view-id>
+ <redirect/>
+ </navigation-case>
+</navigation-rule>]]></programlisting>
+ </example>
+
<para>
- You can either use a templated outcome, with JSF EL expressions as the request parameter
- values:
+ Then the form would have looked like this:
</para>
<example>
- <programlisting><![CDATA[@Name("searchAction")
-public class SearchAction
-{
-
- private String searchPattern;
-
- public String getSearchPattern()
- {
- return searchPattern;
- }
-
- public void setSearchPattern(String searchPattern)
- {
- this.searchPattern = searchPattern;
- }
-
- public String search()
- {
- return "/search.xhtml?searchPattern=#{searchAction.searchPattern}");
- }
-
-}]]></programlisting>
+ <programlisting><![CDATA[<div id="search">
+ <h:form>
+ <h:inputText value="#{searchAction.searchPattern}"/>
+ <h:commandButton value="Search" action="searchResults"/>
+ </h:form>
+</div>]]></programlisting>
</example>
<para>
- Or, if that feels too magical, you can inject and call the <literal>redirect</literal>
- component directly:
+ But when we redirect, we need to include the values submitted with the form as
+ request parameters, to get a bookmarkable URL like
+ <literal>http://localhost:8080/seam-blog/search.seam?searchPattern=seam</literal>.
+ JSF does not provide an easy way to do this, but Seam does. We use a Seam
+ <emphasis>page parameter</emphasis>, defined in <literal>WEB-INF/pages.xml</literal>:
</para>
<example>
- <programlisting><![CDATA[@Name("searchAction")
-public class SearchAction
-{
-
- @In(create=true)
- private Redirect redirect;
-
- private String searchPattern;
-
- public String getSearchPattern()
- {
- return searchPattern;
- }
-
- public void setSearchPattern(String searchPattern)
- {
- this.searchPattern = searchPattern;
- }
-
- public void search()
- {
- redirect.setViewId("/search.xhtml");
- redirect.setParameter("searchPattern", searchPattern);
- redirect.execute();
- }
-
-}]]></programlisting>
+ <programlisting><![CDATA[<pages>
+ <page view-id="/search.xhtml">
+ <param name="searchPattern" value="#{searchService.searchPattern}"/>
+ </page>
+ ...
+</pages>]]></programlisting>
</example>
<para>
+ This tells Seam to include the value of <literal>#{searchService.searchPattern}</literal>
+ as a request parameter named <literal>searchPattern</literal> when redirecting to
+ the page, and then re-apply the value of that parameter to the model before rendering
+ the page.
+ </para>
+
+ <para>
The redirect takes us to the <literal>search.xhtml</literal> page:
</para>
@@ -3012,36 +2995,44 @@
</para>
<example>
- <programlisting><![CDATA[@Name("searchResults")
+ <programlisting><![CDATA[@Name("searchService")
public class SearchService
{
@In(create=true)
private EntityManager entityManager;
- @RequestParameter
private String searchPattern;
- private List<BlogEntry> searchResults;
-
- @Create
- public void initSearchResults()
+ @Factory("searchResults")
+ public List<BlogEntry> getSearchResults()
+ {
+ if (searchPattern==null)
+ {
+ return null;
+ }
+ else
{
- searchResults = entityManager.createQuery("from BlogEntry be where lower(be.title) like :searchPattern or lower(be.body) like :searchPattern order by be.date desc")
+ return entityManager.createQuery("select be from BlogEntry be where lower(be.title) like :searchPattern or lower(be.body) like :searchPattern order by be.date desc")
.setParameter( "searchPattern", getSqlSearchPattern() )
.setMaxResults(100)
.getResultList();
}
+ }
private String getSqlSearchPattern()
{
return searchPattern==null ? "" : '%' + searchPattern.toLowerCase().replace('*', '%').replace('?', '_') + '%';
}
- @Unwrap
- public List<BlogEntry> getSearchResults()
+ public String getSearchPattern()
+ {
+ return searchPattern;
+ }
+
+ public void setSearchPattern(String searchPattern)
{
- return searchResults;
+ this.searchPattern = searchPattern;
}
}]]></programlisting>
@@ -3066,52 +3057,55 @@
<example>
<programlisting><![CDATA[@Name("entryAction")
- at Scope(ScopeType.STATELESS)
+ at Scope(STATELESS)
public class EntryAction
{
@In(create=true)
private Blog blog;
- @RequestParameter
- private String blogEntryId;
-
- @Out(scope=ScopeType.EVENT, required=false)
+ @Out
private BlogEntry blogEntry;
-
- public void getBlogEntry()
- {
- blogEntry = blog.getBlogEntry(blogEntryId);
- if (blogEntry==null)
+ public void loadBlogEntry(String id) throws EntryNotFoundException
{
- HttpError.instance().send(HttpServletResponse.SC_NOT_FOUND);
- }
+ blogEntry = blog.getBlogEntry(id);
+ if (blogEntry==null) throw new EntryNotFoundException(id);
}
}]]></programlisting>
</example>
<para>
- The page action must be declared a file called <literal>pages.xml</literal>:
+ Page actions are also declared in <literal>pages.xml</literal>:
</para>
<example>
<programlisting><![CDATA[<pages>
- <page view-id="/entry.xhtml" action="#{entryAction.getBlogEntry}"/>
+ ...
+
+ <page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry(blogEntry.id)}">
+ <param name="blogEntryId" value="#{blogEntry.id}"/>
+ </page>
+
<page view-id="/post.xhtml" action="#{loginAction.challenge}"/>
+
<page view-id="*" action="#{blog.hitCount.hit}"/>
+
</pages>]]></programlisting>
</example>
<para>
- (Notice that the example is using page actions for some other functionality—the
- login challenge, and the page counter.)
+ Notice that the example is using page actions for some other functionality—the
+ login challenge, and the pageview counter. Also notice the use of a parameter in
+ the page action method binding. This is not a standard feature of JSF EL, but Seam
+ lets you use it, not just for page actions, but also in JSF method bindings.
</para>
<para>
- When the <literal>entry.xhtml</literal> page is requested, Seam first runs the page
- action, which retrieves the needed data—the <literal>blogEntry</literal>—and
- places it in the Seam event context. Next, the following is rendered:
+ When the <literal>entry.xhtml</literal> page is requested, Seam first binds the page parameter
+ <literal>blogEntryId</literal> to the model, then runs the page action, which retrieves the
+ needed data—the <literal>blogEntry</literal>—and places it in the Seam event context.
+ Finally, the following is rendered:
</para>
<example>
@@ -3121,7 +3115,7 @@
<h:outputText escape="false" value="#{blogEntry.body}"/>
</div>
<p>
- [Posted on
+ [Posted on 
<h:outputText value="#{blogEntry.date}">
<f:convertDateTime timezone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
</h:outputText>]
@@ -3129,6 +3123,62 @@
</div>]]></programlisting>
</example>
+ <para>
+ If the blog entry is not found in the database, the <literal>EntryNotFoundException</literal>
+ exception is thrown. We want this exception to result in a 404 error, not a 505, so we
+ annotate the exception class:
+ </para>
+
+ <example>
+ <programlisting><![CDATA[@ApplicationException(rollback=true)
+ at HttpError(errorCode=HttpServletResponse.SC_NOT_FOUND)
+public class EntryNotFoundException extends Exception
+{
+ EntryNotFoundException(String id)
+ {
+ super("entry not found: " + id);
+ }
+}]]></programlisting>
+ </example>
+
+ <para>
+ An alternative implementation of the example does not use the parameter in the method binding:
+ </para>
+
+ <example>
+ <programlisting><![CDATA[@Name("entryAction")
+ at Scope(STATELESS)
+public class EntryAction
+{
+ @In(create=true)
+ private Blog blog;
+
+ @In @Out
+ private BlogEntry blogEntry;
+
+ public void loadBlogEntry() throws EntryNotFoundException
+ {
+ blogEntry = blog.getBlogEntry( blogEntry.getId() );
+ if (blogEntry==null) throw new EntryNotFoundException(id);
+ }
+
+}]]></programlisting>
+
+ <programlisting><![CDATA[<pages>
+ ...
+
+ <page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry}">
+ <param name="blogEntryId" value="#{blogEntry.id}"/>
+ </page>
+
+ ...
+</pages>]]></programlisting>
+ </example>
+
+ <para>
+ It is a matter of taste which implementation you prefer.
+ </para>
+
</section>
</section>
More information about the jboss-cvs-commits
mailing list