6 Our First Use Case: “Display and Edit Campaign”

In the following section, we will implement the use case Display and edit campaign. First, we will turn our attention to the controller class and a class for supplying sample data. After that, we’ll familiarize ourselves with the JSF UI components that are required to implement the input screen for the use case. Finally, we’ll program the Facelet of the view and try it out for ourselves.

For your convenience, we provide a project in the matching state in a temporary workspace. As usual you can also continue to work in your own Codenvy workspace.

Start Cloud IDE

My-Campaign after Chapter 5 (Template & RLC)


6.1 Backing Beans

Backing beans are Java classes that execute tasks behind the scenes of the view. Backing beans can be used as controllers or as a component of the model for storing session data.

Our project will use CDI for the backing beans. What follows is a summary of the most important elements of this new component technology, which will be expanded upon in more detail in our Cloud Tutorial CDI in a Day.

The @Named Annotation

The annotation @Named (javax.inject.Named), which must be placed before the class declaration of a bean, enables us to reference it in a Facelet using the class name beginning with a lower case letter. The annotation also offers us the opportunity to enter a name that differs from the class name. However, we’ll refrain from exploring this particular topic in any more detail at this point.

The Scope @SessionScoped

The scope – and therefore the lifecycle – of a CDI bean can be defined using annotations. The scope can also be viewed as the context of the object, which is the reason for the presence of the C in the acronym CDI. This annotation must also precede the class declaration for the bean. All annotations belong to the package javax.enterprise.context. We will begin by using the session-based lifecycle (@SessionScoped). CDI offers a wide assortment of different scopes – we’ll get to know them better in our Cloud Tutorial CDI in a Day, where we’ll also discuss the use of scopes for our beans in detail and look at ways to optimize them.

@SessionScoped

The session is defined as the lifecycle of a CDI bean. This means that logically the same bean is used throughout a user session. All requests within a session share the status of the bean. The data stored in the bean is stored independently of the view for the duration of the session.

We can now move to the implementation. We will begin by programming the controller for our use case and then the backing bean for the sample data.

The ListCampaignsController Class

We have already established that the controller contains the session lifecycle (@SessionScoped). Controller classes must define the control logic for every possible interaction of the user with the view. The class therefore possesses a method for every possible user interaction. The convention is for all of these methods to begin with the prefix do. In each of these methods, we will add output to the console in order to be able to check the functionality more easily (see ‎Listing 6-1).

package press.turngeek.mycampaign.controller;

import press.turngeek.mycampaign.model.Campaign;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;

@SessionScoped
@Named
public class ListCampaignsController implements Serializable {

	private static final long serialVersionUID = 8693277383648025822L;

	public String doAddCampaign() {
		System.out.println("Add Campaign");
		return Pages.EDIT_CAMPAIGN;
	}
	public String doEditCampaign(Campaign campaign) {
		System.out.println("Edit Campaign "+campaign);
		return Pages.EDIT_CAMPAIGN;
	}
	public String doEditDonationForm(Campaign campaign) {
		System.out.println("Edit Donation Form of Campaign "+campaign);
		return Pages.EDIT_DONATION_FORM;
	}
	public String doListDonations(Campaign campaign) {
		System.out.println("List Donations of Campaign "+campaign);
		return Pages.LIST_DONATIONS;
	}
	public void doDeleteCampaign(Campaign campaign) {
		System.out.println("Deletion not implemented, yet!");
	}
}

Listing 6-1     ListCampaignsController class

At the end of each method is a return command that returns the name of the next view to be accessed as a String. This is how explicit navigation is enabled within JSF applications. We haven’t yet defined another view to navigate to; however, views will be added continuously throughout the rest of the chapter. We will prepare for that now by adding the names of the upcoming views to the class Pages (see ‎Listing  6-2). We must remember to stick to these names when we come to program the corresponding Facelets later. This means, for example, that the Facelet for the list of campaigns must be added to the file listCampaigns.xhtml.

An exception here is the method doDeleteCampaign, which does not possess a return value. The reason for this is that – in contrast to the other methods – it is used within the Facelet as an actionListener method. An actionListener method performs a particular logic and generally only changes the statuses and data of the backing beans that represent the current view. In such cases, navigation to another view is not necessary.

package press.turngeek.mycampaign.controller;

public class Pages {
	public static final String LIST_CAMPAIGNS = "listCampaigns";
	public static final String EDIT_CAMPAIGN = "editCampaign";
	public static final String EDIT_DONATION_FORM = "editDonationForm";
	public static final String LIST_DONATIONS = "listDonations";
	public static final String DONATE_MONEY = "donateMoney";
}

Listing 6-2     Pages class

The CampaignListProducer Class

The class CampaignListProducer, as shown in ‎Listing  6-3, simulates the yet-to-be-created database connection by providing a method that supplies sample data. More precisely, the method getCampaigns returns a list of campaigns. The class contains a further method, createMockCampaigns, in which campaigns and associated donations are explicitly created and are stored in a list. For the list, we will use the Java API class java.util.LinkedList. The method getCampaigns can then be used to request the campaigns as a list. The class uses the lifecycle @SessionScoped, since the data for the complete user session should exist.

package press.turngeek.mycampaign.data;

import press.turngeek.mycampaign.model.Account;
import press.turngeek.mycampaign.model.Campaign;
import press.turngeek.mycampaign.model.Donation;
import press.turngeek.mycampaign.model.Donation.Status;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

@SessionScoped
@Named
public class CampaignListProducer implements Serializable {
	private static final long serialVersionUID = -182866064791747156L;
	private List<Campaign> campaigns;

	public CampaignListProducer() {
		campaigns = createMockCampaigns();
	}
	public List<Campaign> getCampaigns() {
		return campaigns;
	}
	public List<Campaign> createMockCampaigns() {
		Donation donation1 = new Donation();
		donation1.setDonorName("John Smith");
		donation1.setAmount(20d);
		donation1.setReceiptRequested(true);
		donation1.setStatus(Status.TRANSFERRED);
		donation1.setAccount(new Account(donation1.getDonorName(), 
			"XXX Bank","DE44876543210000123456"));
		Donation donation2 = new Donation();
		donation2.setDonorName("Peter Jones");
		donation2.setAmount(30d);
		donation2.setReceiptRequested(false);
		donation2.setStatus(Status.IN_PROCESS);
		donation2.setAccount(new Account(donation2.getDonorName(), 
			"YYY Bank","DE44864275310000654321"));
		List<Donation> spenden = new LinkedList<>();
		spenden.add(donation1);
		spenden.add(donation2);
		Campaign campaign1 = new Campaign();
		campaign1.setName("Shirts for the U19 Team");
		campaign1.setTargetAmount(1000d);
		campaign1.setAmountDonatedSoFar(258d);
		campaign1.setDonationMinimum(20d);
		campaign1.setId(1L);
		campaign1.setAccount(new Account("Robert Cook", "ABC Bank",
			"DE44123456780100200300"));
		campaign1.setDonations(spenden);
		Campaign campaign2 = new Campaign();
		campaign2.setName("Wheelchair for Maria");
		campaign2.setTargetAmount(2500d);
		campaign2.setAmountDonatedSoFar(742d);
		campaign2.setDonationMinimum(25d);
		campaign2.setId(2L);
		campaign2.setAccount(campaign1.getAccount());
		campaign2.setDonations(spenden);
		List<Campaign> ret = new LinkedList<>();
		ret.add(campaign1);
		ret.add(campaign2);
		return ret;
	}
}

Listing 6-3     CampaignListProducer class

At this point, we have implemented the controller logic and data delivery for the first use case. Soon, we will turn to the view declaration  for the same use case. Before implementing this, however, we will use the following section to acquaint ourselves with a few more JSF technologies.

6.2 JSF Technologies for the First Use Case

We require a little more knowledge about JSF to proceed with the implementation. Firstly, we must find out how to reference backing beans within a Facelet, which we can do with the help of the Expression Language (EL). Secondly, a small number of additional UI components are required to enable us to implement the visual characteristics and functionality of the use case according to its design.

Expression Language

Facelets are generally defined as the view declaration language of JSF pages. In the context of such a language, a connection to the logic and data of an application is of integral importance. This access should be as simple as possible. For this reason, the developers of JSF avoided using Java for this and instead defined a new language with a simpler syntax, the Expression Language (EL), whose syntax is based on dot notation. Each bean is referenced uniquely within the Facelet using its name. Dot notation can then be used to access the properties and methods of a bean. This is something we are already familiar with from Java, where an object variable or class name can likewise be used to access their elements. Within Facelets, EL expressions are introduced by the number sign (#) and should be enclosed by curly brackets: #{EL-Expression}.

An EL expression can contain a value expression, a method expression or an arithmetical or logical expression. Combinations are also possible. In our first input screen, we require value expressions – which will be used to assign data to particular UI components – and method expressions, to reference   methods as event handlers for UI components.

We often need to display the current value of a particular bean property in a UI component. Sometimes, we also need to edit it. Each bean class has a getter and a setter method hidden “behind the scenes” for when a value needs to be accessed. Convention dictates that a pair of these methods must exist for every property of a bean. The property name comes after the prefix set/get. Within the EL expression, we can address a property just by using this name – that is, without the prefixes set and get. In order to be able to access the properties and methods of a bean at all, the bean name must be known and must precede the EL expression. Beans can only be addressed if the annotation @Named is used in the bean’s class declaration. The name of a bean can be explicitly set (e.g. @Named("myBean")). If it is not, the bean name will match the class name given in lower case letters at the beginning.

Let’s look at an example from our program so far. The bean CampaignListProducer encapsulates a list of campaigns (see ‎Listing  6-3) and provides a corresponding getter method for the list attribute. The bean is annotated with @Named and will later be embedded in the Facelet listCampaigns.xhtml, where its job is to supply the campaigns to be displayed. The bean’s list of campaigns can be embedded in the Facelet using the Expression Language. The list of campaigns can be bound to a UI component using the expression #{campaignListProducer.campaigns}. In this case, access to the UI components is read-only, since we have not implemented a setter method for the attribute. However, this is an exception. As a rule, the binding of a UI component to a bean attribute allows the value to be edited as well as read.

Method expressions are similiar to value expressions in terms of their syntax. Like value expressions, they are preceded by the bean name; however, they differ in that the complete method name should come after the dot. If, for example, we want to reference the method doAddCampaign of the bean class ListCampaignsController from ‎Listing 6-1 as the handler of a UI component, the EL expression will be written as follows: #{campaignsListController.doAddCampaign}. Though the EL actually has much more to offer than this, the above is all we require to understand our implementation.

UI Components

We’ll now look at the UI components we need. In order to implement the input screen of our use case, we will first require UI components such as buttons or links that access program logic via handler methods. Our aim is to be able to reach the campaign-related functions using links and to add a campaign with a button.

Further to this, we need to be able to display texts (labels) for the headings and other purposes and to be able to represent data in table form. The components should be flexible enough to allow the number of columns in the table to be determined at page-creation time. Last but not least, we want to display the amounts with the correct formatting. This means that we will need to convert numerical values into the correct format for Euro amounts.

Form Components

To be able to enter data and submit it to the server, we will need a form. Using the <h:form> tag in a Facelet, JSF can create an HTML form into which UI components can be embedded as desired. For our input screen, a single form beginning with <h:form> and ending with </h:form> will suffice.

<h:form>  in a nutshell

This tag is rendered to an HTML form that submits its data via an HTTP POST. A form must contain at least one component that is capable of triggering a Faces request (e.g. <h:commandButton> or <h:commandLink>, as you will see below).

Selected attributes:

acceptcharset: gives a list of character sets accepted by the server for the input data of a form, e.g. <h:form acceptcharset="ISO-8859-1">

Links and Button Components

The tags <h:commandLink> and <h:commandButton> behave like links or buttons that trigger Action Events when clicked by the user.

<h:commandButton> and <h:commandLink> in a nutshell

In a form, these tags are rendered to a button or link that starts an HTTP POST request by default when clicked.

Selected attributes:

value: Labeling a link or button

action: EL expression for binding an action method of a bean

actionListener: EL expression for binding an actionListener method of a bean

Action events are objects that are created by clicking a link or button and that store information about this event. In addition, JSF recognizes other types of events that are fired at the runtime of a JSF application, with value changed events being one example. These are created by UI components when the value stored by the component is changed. Event handling is accomplished by using methods as event handlers. In the case of action events, there are two types of event handler methods: action methods and actionListener methods. Using EL expressions, the methods are stored as values for the action or actionListener attributes of the respective JSF components. An action method has a return value of type String, which controls navigation between different pages of the application. An actionListener method has no return value (void).

The attribute value can be used to specify a link label or the name of a button. Each form requires at least one such component. Both action and actionListener methods can have parameters. actionListener methods can also declare a parameter of the type ActionEvent from the package javax.faces.event. This class can then be used to request information in the handler methods about the component that triggers the event. The following two examples show the binding of both an action and an actionListener method.

 

<h:commandButton value="Add Campaign" action="#{campaignListController.doAddCampaign}" />

<h:commandLink value="x" actionListener="#{campaignListController.doDeleteCampaign(campaign)}" />

Action Method or actionListener Method?

Action methods are generally used in situations where we want to influence navigation. To be able to do this, the return value of an action method must contain the name of the subsequent view.

actionListener methods, in contrast, offer easy access to the action event and thus to the component that triggers the event. The application does not navigate to another view, but the content of the current view may be manipulated by the listener method.

Output Text Components

The tag <h:outputText> can be used to embed text in the page.

<h:outputText> in a nutshell

This tag adds text to the page to be created.

id: Unique name of the text component

value: Text or EL expression for binding a bean attribute or method to the component

The use of the tag thus makes particular sense in situations where converters are used or where an ID needs to be be assigned for the component. This ID can then be used to refer to the component in other places. If neither of the above is necessary, the text or EL expression can be added directly in the Facelet. In the following example, the value of the attribute value is embedded in the page directly as text.

<h:outputText value="bisher gespendet " />

The tag enables the use of converters. In the following example, the number stored in the bean property amountDonatedSoFar should be displayed as the amount of currency.

 

<h:outputText value="#{campaign.amountDonatedSoFar}">

<f:convertNumber type="currency" currencyCode="EUR"/>

</h:outputText>

Components for Formatting Numbers

For the most part, it is not sufficient for our application to merely show the numbers themselves. Numbers have their own semantics, and formatting conventions are often an important aspect. The formatting can be carried out using special JSF converters. In the case of numbers, <f:convertNumber> is commonly used.

<f:convertNumber> in a nutshell

These configurable components enable the user to create settings for the output of numbers, currency amounts and percentages. Selected attributes:

type: Type of formatting: number, currency or percentage amount

currencyCode: Currency code in accordance with ISO 4217

for: Refers to the component to be converted by specifying its  id

maxFractionDigits: Maximum number of places after the comma

minFractionDigits: Minimum number of place after the comma

In our case, the numbers used stand for amounts in euros, and we want to display them as such. The tag takes care of the conversion for us. We must configure only one instance of the tag – which sets special attributes for the displaying of euros – and embed it in our output components (h:outputText). In the example, the value of the property donatedAmountSoFar belonging to the bean campaign is displayed as a euro amount. This means that amounts in the thousands are broken up with a dot, euros and cents are separated by a comma and the euro sign appears at the beginning.

As an alternative to the nesting of the corresponding tag, the converter’s reference to a tag can be accomplished with the tag ID and the attribute for. The following example shows the relation for/id:

We will discuss and familiarize ourselves with the general concept of validation and conversion later on. In this context, we will also explore more validators and converters provided by JSF.

Data Table Components

The <h:dataTable> tag allows us to represent data in tabular form. The entire structure is rendered to HTML for the browser using the HTML tag <table>. The columns of the table are defined using the tag <h:column>. The tag <h:facet> is used to insert headers and footers.

<h:dataTable&gt;, <h:column>, <f:facet> in a nutshell

We use these components to display lists of identical objects in tabular form.

<h:dataTable>

The outer tag of the data table

Selected attributes:

value: Data to be displayed. This generally takes the form of an EL expression that references an attribute of a bean containing a collection of objects.

var: Name of the variables that represent the data object of a row. The variables can be used in EL expressions.

<h:column>

Represents a column in the data table and is therefore used within <h:dataTable>. The number of successive <h:column> tags determines the number of columns per row of the table.

<f:facet>

Caption for  enclosing elements. <f:facet> can be used within a <h:column> tag to specify a header or footer for the column.

Selected attributes:

name: Type of labelling (header/footer/caption). Determines whether the label is a header or a footer.

The number of rows is dependent on the data source, which can be passed to the tag using the attribute value. In the current iteration of our web application, we will access the campaigns of the backing bean CampaignListProducer using an EL expression. Accordingly, the table contains a list of campaigns. Each campaign is represented as a row in the table. Access to the individual campaigns is accomplished with a special variable. The tag uses the attribute var to introduce a campaign variable of the type Campaign. The variable corresponds to a control variable in a loop and represents the current campaign when processing the list. These variables can be used to access the properties of a campaign for each row.

Labels for columns and tables can be inserted using the tag. Possible facets are header, footer and caption (for the table name). We want to create the column headers, so we will use the tag within the tag and set the name attribute’s value to header. The following Facelet fragment outlines how the data table might be structured. However, we can only see the definition for the first column in the example.

<h:dataTable value="#{campaignListProducer.campaigns}" var="campaign">
	<h:column>
		<f:facet name="header">
			<h:outputText value="Name" />
		</f:facet>
		<h:outputText value="#{campaign.name}" />
	</h:column>
...
</h:dataTable>

6.3 Our First View!

On the basis of the template provided, we will now create the user interface for the use case Display and edit campaign. To do this, we need once again to think back to the appearance of the input screen as laid out in the requirements of the screen.

Our input screen should be used as the content part of the template of the current contract and in accordance with the language preferences set in the browser. The contract is set using the <f:view> tag. A reference to the template is generated via the <ui:composition> tag. We can reference the data contained in our template using the attribute template. We also define the necessary XML namespaces for the JSF tag libraries.

Our next task is to overwrite the content area of the template within the <ui:composition> tag. The <ui:define> tag enables us to do this. We reference the content area with the attribute name, whose value we must set to content. All of the previously-mentioned UI components (dataTable, outputText, commandLink, commandButton and convertNumber) should now appear within this tag in the order we specify. ‎Listing  6-4 shows the complete Facelet.

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
	xmlns:f="http://xmlns.jcp.org/jsf/core"
	xmlns:h="http://xmlns.jcp.org/jsf/html">
<body>
	<f:view contracts="#{view.locale.language}">
		<ui:composition template="/template.xhtml">
			<ui:define name="content">
				<h1>My Campaigns</h1>
				<h:form>
					<h:dataTable value="#{campaignListProducer.campaigns}"
						var="campaign">
						<h:column>
							<h:commandLink value="x"
								actionListener=
									"#{listCampaignsController
										.doDeleteCampaign(campaign)}" />
						</h:column>
						<h:column>
							<f:facet name="header">Name</f:facet>
							<h:outputText value="#{campaign.name}" />
						</h:column>
						<h:column>
							<f:facet name="header">Target Amount</f:facet>
							<h:outputText value="#{campaign.targetAmount}">
								<f:convertNumber type="currency" 
									currencyCode="EUR" />
							</h:outputText>
						</h:column>
						<h:column>
							<f:facet name="header">Donated so far</f:facet>
							<h:outputText value="#{campaign.amountDonatedSoFar}">
								<f:convertNumber type="currency" 
									currencyCode="EUR" />
							</h:outputText>
						</h:column>
						<h:column>
							<h:commandLink value="Edit"
								action="#{listCampaignsController
											.doEditCampaign(campaign)}" />
						</h:column>
						<h:column>
							<h:commandLink value="List donations"
								action="#{listCampaignsController
										.doListDonations(campaign)}" />
						</h:column>
						<h:column>
							<h:commandLink value="Form"
								action="#{listCampaignsController
										.doEditDonationForm(campaign)}" />
						</h:column>
					</h:dataTable>
					<h:commandButton value="Add campaign"
						action="#{listCampaignsController.doAddCampaign}" />
				</h:form>
			</ui:define>
		</ui:composition>
	</f:view>
</body>
</html>

Listing 6-4    Facelet listCampaigns.xhtml

The attentive reader will have noticed that it is necessary for some of the controller’s methods to pass a campaign object. Since the methods are referenced using an EL expression, we are faced with the challenge of how an object can be passed at this point. The answer is very simple: The campaign variable introduced with the <h:dataTable> tag represents one campaign per row. We can use the name of this variable directly in the EL expression for the handler methods.

<h:commandLink value="Form" action="#{listCampaignsController.doEditDonationForm(campaign)}" />

We will save the file listCampaigns.xhtml and all following XHTML files in the project structure in the directory srcmainwebapp.

6.4 The Start – A First Milestone

Before we restart our WildFly runner, we should change the entry page of our application from index.xhtml to listCampaigns.xhtml. We can make this the case for our application by changing the HTML page index.html as follows.

<html>
	<head>
		<meta http-equiv="Refresh" content="0; URL=listCampaigns.jsf">
	</head>
</html>

Listing 6-5     HTML page index.html

The first use case is now complete – and it’s the perfect time for us to try out our work live. Start the WildFly runner again and access the provided URL.  ‎Fig. 6-1 shows the browser content that should appear on your screen.

How the campaign list should appear in the browser.
Fig. 6-1     How the campaign list should appear in the browser

The following output log from the WildFly console – which is intended to serve as an example – is created by clicking the button Add campaign and then clicking sequentially on the the links Edit, List donations and Form for the campaign Shirts for the U19 Team. Thus, the output corresponds exactly with what we specified in the methods of the controller bean via System.out.println. The Campaign object is serialized and output accordingly using the toString() method of the class java.lang.

[STDOUT] 21:37:27,687 INFO [stdout] (default task-13) Add Campaign
[STDOUT] 21:37:29,879 INFO [stdout] (default task-14) Edit Campaign press.turngeek.mycampaign.model.Campaign@7ecfcdae
[STDOUT] 21:37:30,708 INFO [stdout] (default task-15) List Donations of Campaign press.turngeek.mycampaign.model.Campaign@7ecfcdae
[STDOUT] 21:37:31,644 INFO [stdout] (default task-16) Edit Donation Form of Campaign press.turngeek.mycampaign.model.Campaign@7ecfcda

Finished? Not Quite!

The page is now looking pretty good. However, we have yet to tackle the issue of internationalization. All of the static text on the page – and all the labels of the buttons and links – are hard-coded with English terms  in the Facelet. As described in chapter 4, we can change this by introducing a keyword pair for each term in the property files of the supported Locales. In addition to this, we must replace the static text in the Facelets with EL expressions. The expressions use the key to display the text in the correct language.

We will first amend the properties file messages_en.properties in the directory srcmainresources with the entries required for the listCampaigns.xhtml Facelet:

# listCampaigns.xhtml
listCampaigns.my_campaigns=My campaigns
listCampaigns.name=Name
listCampaigns.target_amount=Target amount
listCampaigns.donated_so_far=Donated so far
listCampaigns.edit=Edit
listCampaigns.list_donations=List donations
listCampaigns.form=Form
listCampaigns.add_campaign=Add campaign

Once we’ve done this, we will store the corresponding German entries in the file messages_de.properties:

# listCampaigns.xhtml
listCampaigns.my_campaigns=Meine Aktionen
listCampaigns.name=Name
listCampaigns.target_amount=Spendenziel
listCampaigns.donated_so_far=bisher gespendet
listCampaigns.edit=Editieren
listCampaigns.list_donations=Spendenliste
listCampaigns.form=Formular
listCampaigns.add_campaign=Aktion hinzufügen

Finally, we will edit the Facelet listCampaigns.xhtml as shown in ‎Listing 6-5: (changes highlighted in yellow):

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
	xmlns:f="http://xmlns.jcp.org/jsf/core"
	xmlns:h="http://xmlns.jcp.org/jsf/html">
<body>
	<f:view contracts="#{view.locale.language}">
		<ui:composition template="/template.xhtml">
			<ui:define name="content">
				<h1>#{msg['listCampaigns.my_campaigns']}</h1>
				<h:form>
					<h:dataTable value="#{campaignListProducer.campaigns}"
						var="campaign">
						<h:column>
							<h:commandLink value="x"
								actionListener=
									"#{listCampaignsController
										.doDeleteCampaign(campaign)}" />
						</h:column>
						<h:column>
							<f:facet name="header">
								#{msg['listCampaigns.name']}
							</f:facet>
							<h:outputText value="#{campaign.name}" />
						</h:column>
						<h:column>
							<f:facet name="header">
								#{msg['listCampaigns.target_amount']}
							</f:facet>
							<h:outputText value="#{campaign.targetAmount}">
								<f:convertNumber type="currency" 
									currencyCode="EUR" />
							</h:outputText>
						</h:column>
						<h:column>
							<f:facet name="header">
								#{msg['listCampaigns.donated_so_far']}
							</f:facet>
							<h:outputText value="#{campaign.amountDonatedSoFar}">
								<f:convertNumber type="currency" 
									currencyCode="EUR" />
							</h:outputText>
						</h:column>
						<h:column>
							<h:commandLink value="#{msg['listCampaigns.edit']}"
								action="#{listCampaignsController
											.doEditCampaign(campaign)}" />
						</h:column>
						<h:column>
							<h:commandLink value=
									"#{msg['listCampaigns.list_donations']}"
								action="#{listCampaignsController
										.doListDonations(campaign)}" />
						</h:column>
						<h:column>
							<h:commandLink value="#{msg['listCampaigns.form']}"
								action="#{listCampaignsController
										.doEditDonationForm(campaign)}" />
						</h:column>
					</h:dataTable>
					<h:commandButton value=
							"#{msg['listCampaigns.add_campaign']}"
						action="#{listCampaignsController.doAddCampaign}" />
				</h:form>
			</ui:define>
		</ui:composition>
	</f:view>
</body>
</html>

Listing 6-6     Facelet listCampaigns.xhtml

In order to try out the changes, we must redeploy the project by stopping and restarting our WildFly runner. Following redeployment, the list of campaigns will be displayed according to the language set in the browser.

We have now successfully implemented the first use case and will focus in the next chapter on the implementation of the remaining cases. Unfortunately, as we will see, the JSF standard does not offer all of the UI components required to implement the input screens. For this reason, we will take a brief venture into the world of JSF extensions (in this case, PrimeFaces). In the real world, the use of additional libraries is commonplace. However, our plan is to stay with the JSF standard and to rely on it to as great an extent as possible  for the implementation of the remaining use cases. Before we do so, we will use the next section to take a look at some of the other technologies required for the rest of our implementation.

Discussion

Use the message board below to give the authors your feedback or to discuss this page’s topic with other readers (in English please!). Please don’t expect the authors to answer directly, but they might update the content of this site according to your feedback.