Tapestry Components for Web 2.0

Preview:

DESCRIPTION

Tapestry Components for Web 2.0. Howard Lewis Ship TWD Consulting, Inc. hlship@gmail.com. What is Tapestry?. Application. Page. Page. Component. Component. Component. Component. Component. Component. Controller. View. Model. Java Beans. HTML template. Dynamic HTML output. - PowerPoint PPT Presentation

Citation preview

1

Tapestry Components for Web 2.0

Howard Lewis Ship

TWD Consulting, Inc.

hlship@gmail.com

2

What is Tapestry?

Application

PagePage

ComponentComponent ComponentComponent

ComponentComponent

3

Controller

ModelView

HTML template Java Beans

Dynamic HTML output

Links / Form Submissions

4

Login Form

5

Border.html<div jwcid="loginDialog"> <div class="dialog"> <form jwcid="form"> <p> Enter your email address and password to log in. </p> <span jwcid="errors@Errors"/> <label jwcid="@FieldLabel" field="component:email"/> <input jwcid="email" size="30"/> <label jwcid="@FieldLabel" field="component:password"/> <input jwcid="password" size="30"/> <input type="submit" value="Login"/> <input jwcid="@Cancel" ajax="true"/> </form> <p> Not registered yet? <a jwcid="register2"> Click here to setup an account</a>. </p> </div></div>

6

Border.html

Placeholder for tacos:Dialog component Dialogs are invisible until un-hidden Active dialogs mask the rest of the page

<div jwcid="loginDialog">

. . .

</div>

7

Border.jwc

Dialog visibility from dialogHidden property of page

Login link: Set dialogHidden to false Re-render just the loginDialog

<component id="loginDialog" type="tacos:Dialog"> <binding name="hidden" value="dialogHidden"/></component>

8

Border.jwc

Invoke the listener method Re-render just the loginDialog component

<component id="login" type="tacos:AjaxDirectLink"> <binding name="listener" value="listener:doShowLogin"/> <binding name="updateComponents" value="{ 'loginDialog' }"/></component>

9

Form component

Tapestry forms are inside a Form component: Unique names & ids for fields (even inside loops) Tracking of user input and errors Handling submit, refresh & cancel AjaxForm: Partial page refreshes

<form jwcid="form"> . . .</form>

10

Form Component

listener: Name of listener method to invoke delegate Object that tracks user input and

errors

<component id="form" type="tacos:AjaxForm"> <binding name="updateComponents" value="{ 'errors' }"/> <binding name="success" value="listener:doLogin"/> <binding name="cancel" value="listener:doCancel"/> <binding name="delegate" value="delegate"/></component>

11

OGNL

Object Graph Navigation Language Expression language used by Tapestry ognl: prefix when used in HTML template No prefix when used in XML file

12

OGNL

Simple: property namesdelegate getDelegate(),

setDelegate() Complex: property paths

poll.title getPoll().getTitle(), getPoll().setTitle()

Much more!

13

TextField Component

@FieldLabel anonymous component of type FieldLabel Nothing in the Login.page file

component:email reference to email component

<label jwcid="@FieldLabel" field="component:email"/>:<input jwcid="email" size="30"/>

14

TextField Component

value property to read and update validators validations to perform displayName Used in error messages, and

by FieldLabel

<component id="email" type="TextField"> <binding name="value" value="email"/> <binding name="validators" value="validators:required"/> <binding name="displayName" value="message:email-label"/></component>

15

Password TextField

<component id="password" type="TextField"> <binding name="value" value="password"/> <binding name="validators" value="validators:required"/> <binding name="displayName" value="message:password-label"/> <binding name="hidden" value="true"/></component>

16

Register Link

literal: value is just a string, not an OGNL expression

<p>Not registered yet? <a jwcid="register">Click here to setup an account</a>. </p>

<component id="register" type="PageLink"> <binding name="page" value="literal:Register"/> </component>

17

Java Classpublic abstract class Border extends BaseComponent{

public abstract String getEmail();

public abstract String getPassword();

. . .

18

Abstract?

Pages are stateful Hold transient data during request Hold persistent data between requests

Pages are expensive to create Pages are pooled

Like database connections

19

No, Really, Abstract?

Tapestry extends abstract class Adds getter, setter, instance variables Adds end-of-request cleanup Lots of injections based on getters and

annotations (or XML)

20

Embedded Components

Injected CodeApplication

Code

Inherited Code

Page Behavior

21

Java Class

getEmail() & setEmail() getPassword() & setPassword()

public abstract class Border extends BaseComponent{

public abstract String getEmail();

public abstract String getPassword();

. . .

22

Listener Methods

Public method Changes server side state Form will re-render

public void doCancel(){ getLoginDialog().hide();}

23

Injecting Services

Service defined in HiveMind IoC Container Can inject Spring beans as easily Keep business logic out of pages / components

@InjectObject("service:epluribus.LoginAuthenticator")public abstract LoginAuthenticator getAuthenticator();

public interface LoginAuthenticator{ User authenticateCredentials(String email, String plaintextPassword);}

24

Listener Methodspublic String doLogin() { String email = getEmail(); String password = getPassword();

User user = getAuthenticator().authenticateCredentials( email, password);

. . .

25

Listener Methods . . .

if (user == null) { getDelegate().record(null, "Invalid user name or password."); return null; }

getIdentity().login(user);

getLoginDialog().hide();}

26

Server Side State: ASO

Application State Objects Global to all pages Stored in HttpSession Created on demand Defined in HiveMind Injected into pages or components

@InjectState("identity")public abstract Identity getIdentity();

27

Identity ASO

public class Identity implements Serializable { . . .

public boolean isLoggedIn() { . . . }

public void login(User user) { . . . } public void logout() { . . . }

public User getUser() { . . . }}

28

Loops, Tables and Actions

29

Home.html

<table class="data-grid" cellspacing="0" jwcid="polls"/>

<div jwcid="pollingEndColumnValue@Block"><span jwcid="@InsertDate" date="ognl:poll.pollingEnd"/></div>

<div jwcid="statusColumnValue@Block"><span jwcid="@Insert" value="ognl:responseCount"/><a jwcid="respond">Respond</a></div>

30

Home.page

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

31

Home.page

Defines a new property on page Alternately: create abstract property

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

32

Home.page

contrib: is name of tapestry-contrib.jar library

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

33

Home.page

source total list of Poll objects

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

34

Home.page

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

columns how to break a Poll object into columns

35

Home.properties

column-id : title : OGNL expression poll.title poll.questionCount null calculated elsewhere

!status status is not sortable

table-columns=\ title:Title:title, \ questions:Questions:questionCount, \ pollingEnd:End of Polling:pollingEnd, \ !status:Status:null

36

Home.page

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

row Property to update with each rendered row (each Poll)

37

Home.page

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

index Stores index into row (used to set row CSS class)

38

Home.page

<property name="rowIndex"/>

<component id="polls" type="contrib:Table"> <binding name="source" value="polls"/> <binding name="columns" value="message:table-columns"/> <binding name="row" value="poll"/> <binding name="index" value="rowIndex"/> <binding name="rowsClass"> rowIndex == 0 ? "first" : null </binding> </component>

rowsClass CSS class value for the <tr> Identify first row to change its formatting

39

Home.html

<table class="data-grid" cellspacing="0" jwcid="polls"/>

<div jwcid="pollingEndColumnValue@Block"><span jwcid="@InsertDate" date="ognl:poll.pollingEnd"/></div>

<div jwcid="statusColumnValue@Block"><span jwcid="@Insert" value="ognl:responseCount"/><a jwcid="respond">Respond</a></div>

40

Home.html

<table class="data-grid" cellspacing="0" jwcid="polls"/>

<div jwcid="pollingEndColumnValue@Block"><span jwcid="@InsertDate" date="ognl:poll.pollingEnd"/></div>

<div jwcid="statusColumnValue@Block"><span jwcid="@Insert" value="ognl:responseCount"/><a jwcid="respond">Respond</a></div>

41

Home.page

DirectLink invokes a listener method when clicked

Can pass parameters into the listener method

<component id="respond" type="DirectLink"> <binding name="listener" value="listener:doRespond"/> <binding name="parameters" value="poll.id"/></component>

42

Home.java

Parameters show up … with proper type (not just String)

@InjectPage("RespondToPoll")public abstract RespondToPoll getRespondToPoll();

public void doRespond(long pollId) { Poll poll = getPollAccess().getPoll(pollId);

// TODO: A few checks, i.e., Poll is active getRespondToPoll().activate(poll);}

43

Creating New Components

44

FCKEditor

"the text editor for the Internet" Open Source http://www.fckeditor.net/

45

FCKEditor

Primarily a JavaScript library: FCKeditor/fckeditor.js

Goal: Component to take place of TextArea

46

FCKEditor Component

FCKEditor.jwc Copy of TextArea.jwc FCKEditor extends TextArea

47

FCKEditor.javaprotected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle){ super.renderFormComponent(writer, cycle);

// Now, we want to work with the script.

PageRenderSupport support = TapestryUtils.getPageRenderSupport(cycle, this);

support.addExternalScript(getEditorScript().getResourceLocation());

String contextPath = getRequest().getContextPath();

String id = getClientId();

String clientObject = "editor_" + id;

StringBuffer buffer = new StringBuffer();

buffer.append(String.format("var %s = new FCKeditor('%s');\n", clientObject, id)); buffer.append(String.format("%s.BasePath = '%s/FCKeditor/';\n", clientObject, contextPath)); buffer.append(String.format("%s.ReplaceTextarea();\n", clientObject));

support.addInitializationScript(buffer.toString());}

48

FCKEditor.java

super.renderFormComponent(writer, cycle);

PageRenderSupport support = TapestryUtils.getPageRenderSupport(cycle, this);

support.addExternalScript( getEditorScript().getResourceLocation());

@Asset("context:FCKeditor/fckeditor.js")public abstract IAsset getEditorScript();

49

FCKEditor.java

String contextPath = getRequest().getContextPath();

String id = getClientId();

String clientObject = "editor_" + id;

50

FCKEditor.java

StringBuffer buffer = new StringBuffer();

buffer.append(String.format("var %s = new FCKeditor('%s');\n", clientObject, id));

buffer.append(String.format("%s.BasePath = '%s/FCKeditor/';\n", clientObject, contextPath));

buffer.append(String.format("%s.ReplaceTextarea();\n",

clientObject));

support.addInitializationScript(buffer.toString());

51

FCKEditor Summary

Easy to knit components & JavaScript Super easy to use:

<input jwcid="@FCKEditor" value="ognl:description"/>

52

Wrap Up

53

More Tapestry Topics

Persistent Page Properties Creating New Components Localization Packaging component libraries Integration with Hibernate and Spring Unit and Integration Testing Extending and Overriding Tapestry

54

ePluribus Source

Via Anonymous SVN:

http://svn.javaforge.com/svn/tapestry/epluribus/trunk

User: anonymous Password: anon

55

Links

Tapestry http://tapestry.apache.org

HiveMind http://jakarta.apache.org/hivemind/ http://hivemind.apache.org/

Tacos http://tacos.sf.net/

OGNL http://www.ognl.org/

Recommended