🧭 Web User Interfaces: Navigation and Events in Jakarta EE
The Story of the Magic Mall
Imagine you’re in a gigantic magic mall. This mall has many rooms (pages), and you need to move between them. Sometimes you walk through doors by yourself, sometimes a friendly guide takes you, and sometimes magic happens when you touch things!
In Jakarta EE Faces, this is exactly how web pages work. Let’s explore!
🚪 Faces Navigation: The Doors of Your App
Think of Faces Navigation as all the doors in your magic mall. When you click a button, you go through a door to another room (page).
How It Works
Every web app needs a way to move from one page to another. Faces gives you three magical doors:
- Implicit Navigation → The automatic door
- Programmatic Navigation → The guide-controlled door
- Faces-config.xml Navigation → The map-planned door
graph TD A["User Clicks Button"] --> B{Which Navigation?} B --> C["Implicit Navigation"] B --> D["Programmatic Navigation"] B --> E["faces-config.xml"] C --> F["Auto-finds Page"] D --> G["Code Decides Page"] E --> H["Config File Decides"]
🔮 Implicit Navigation: The Smart Automatic Door
Analogy: Imagine a door that knows where you want to go just by hearing the room name!
What Is It?
Implicit Navigation is the easiest way to navigate. You just tell the button the page name, and Faces finds it automatically.
Simple Example
<h:commandButton
value="Go to Welcome"
action="welcome" />
What happens:
- User clicks the button
- Faces looks for
welcome.xhtml - User goes to that page!
The Magic Rule
| You Write | Faces Looks For |
|---|---|
"home" |
home.xhtml |
"about" |
about.xhtml |
"contact" |
contact.xhtml |
No configuration needed! The name in action becomes the page name + .xhtml.
Real-World Example
<!-- login.xhtml -->
<h:form>
<h:commandButton
value="Login"
action="dashboard" />
<h:commandButton
value="Register"
action="register" />
</h:form>
After clicking “Login” → Goes to dashboard.xhtml
After clicking “Register” → Goes to register.xhtml
🎮 Programmatic Navigation: The Smart Guide
Analogy: Instead of automatic doors, you have a smart guide (your Java code) who decides which room to take you to!
What Is It?
Sometimes you need to think before navigating. Maybe:
- Check if login was successful
- Decide based on user’s age
- Go different places based on what happened
How It Works
Your button calls a Java method that returns the page name.
Example: Login Check
The Button (login.xhtml):
<h:commandButton
value="Login"
action="#{loginBean.checkLogin}" />
The Java Bean:
@Named
@RequestScoped
public class LoginBean {
private String username;
private String password;
public String checkLogin() {
if (isValidUser()) {
return "dashboard"; // Go here!
} else {
return "error"; // Go here instead!
}
}
}
The Decision Flow
graph TD A["User Clicks Login"] --> B["checkLogin runs"] B --> C{Valid User?} C -->|Yes| D["Return &#39;dashboard&#39;"] C -->|No| E["Return &#39;error&#39;"] D --> F["Go to dashboard.xhtml"] E --> G["Go to error.xhtml"]
Using NavigationHandler Directly
For extra control, use the NavigationHandler:
@Inject
FacesContext facesContext;
public void goSomewhere() {
facesContext
.getApplication()
.getNavigationHandler()
.handleNavigation(
facesContext,
null,
"targetPage"
);
}
⚡ Faces Event Handling: When Magic Happens
Analogy: In our magic mall, when you touch things, magic happens! These are events.
There are three types of magic touches:
| Event Type | When It Happens |
|---|---|
| Action Events | Button clicks |
| Value Change Events | Dropdown changes, typing |
| Phase Events | Behind-the-scenes |
🎯 Action Events: The Button Click Magic
Analogy: When you press a magic button, a spell is cast!
What Is It?
Action Events happen when users click buttons or links. Two ways to handle them:
Way 1: action (Navigate After)
<h:commandButton
value="Save"
action="#{userBean.save}" />
public String save() {
// Save data
return "success"; // Navigate to success.xhtml
}
Way 2: actionListener (React Immediately)
<h:commandButton
value="Like"
actionListener="#{postBean.like}" />
public void like(ActionEvent event) {
likeCount++;
// No navigation, stay on same page
}
When to Use Which?
Use action |
Use actionListener |
|---|---|
| Need to navigate | Stay on same page |
| After form submit | Quick reactions |
| Returns page name | Returns nothing |
Complete Example
<h:form>
<h:commandButton
value="Add to Cart"
actionListener="#{cartBean.addItem}"
action="#{cartBean.checkout}" />
</h:form>
What happens:
addItem()runs first (adds item)checkout()runs next (navigates)
🔄 Value Change Events: The Watcher Magic
Analogy: Imagine a magic mirror that notices when you change your outfit and reacts!
What Is It?
Value Change Events trigger when form values change. Great for:
- Updating related dropdowns
- Showing/hiding sections
- Validating as you type
Basic Example
<h:selectOneMenu
value="#{bean.country}"
onchange="submit()"
valueChangeListener="#{bean.countryChanged}">
<f:selectItem
itemValue="us"
itemLabel="USA" />
<f:selectItem
itemValue="uk"
itemLabel="UK" />
</h:selectOneMenu>
public void countryChanged(
ValueChangeEvent event) {
String oldVal =
(String) event.getOldValue();
String newVal =
(String) event.getNewValue();
// Load cities for new country
loadCities(newVal);
}
The Flow
graph TD A["User Changes Value"] --> B["Form Submits"] B --> C["valueChangeListener Runs"] C --> D["Get Old Value"] C --> E["Get New Value"] D --> F["React to Change"] E --> F
Using f:valueChangeListener Tag
<h:inputText value="#{bean.email}">
<f:valueChangeListener
type="com.app.EmailValidator" />
</h:inputText>
AJAX Way (Modern Approach)
<h:selectOneMenu value="#{bean.country}">
<f:ajax
event="change"
listener="#{bean.countryChanged}"
render="cityDropdown" />
<f:selectItems value="#{bean.countries}" />
</h:selectOneMenu>
<h:selectOneMenu
id="cityDropdown"
value="#{bean.city}">
<f:selectItems value="#{bean.cities}" />
</h:selectOneMenu>
🌊 Phase Events: The Behind-the-Scenes Magic
Analogy: Before you enter a room in the magic mall, invisible helpers clean it, turn on lights, and prepare everything!
What Is It?
Jakarta Faces has a lifecycle - a series of steps that happen every time a page is requested. Phase Events let you hook into these steps.
The 6 Phases
graph TD A["1. RESTORE VIEW"] --> B["2. APPLY REQUEST"] B --> C["3. VALIDATE"] C --> D["4. UPDATE MODEL"] D --> E["5. INVOKE APP"] E --> F["6. RENDER RESPONSE"]
| Phase | What Happens |
|---|---|
| Restore View | Build/restore the page |
| Apply Request | Get form data |
| Process Validations | Check if data is valid |
| Update Model | Put data in beans |
| Invoke Application | Run your methods |
| Render Response | Show the result |
Creating a Phase Listener
public class MyPhaseListener
implements PhaseListener {
@Override
public void beforePhase(
PhaseEvent event) {
System.out.println(
"Before: " + event.getPhaseId());
}
@Override
public void afterPhase(
PhaseEvent event) {
System.out.println(
"After: " + event.getPhaseId());
}
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}
Registering It
In faces-config.xml:
<lifecycle>
<phase-listener>
com.app.MyPhaseListener
</phase-listener>
</lifecycle>
Common Use Cases
| Use Case | Listen To |
|---|---|
| Security check | RESTORE_VIEW |
| Logging | ANY_PHASE |
| Performance | RENDER_RESPONSE |
🎁 Putting It All Together
Here’s a complete example showing all concepts:
The Page (shop.xhtml)
<h:form>
<!-- Value Change Event -->
<h:selectOneMenu
value="#{shopBean.category}">
<f:ajax
event="change"
listener="#{shopBean.catChanged}"
render="products" />
<f:selectItems
value="#{shopBean.categories}" />
</h:selectOneMenu>
<!-- Action Event (stays on page) -->
<h:commandButton
value="Add to Cart"
actionListener="#{shopBean.addToCart}">
<f:ajax render="cartCount" />
</h:commandButton>
<!-- Implicit Navigation -->
<h:commandButton
value="View Cart"
action="cart" />
<!-- Programmatic Navigation -->
<h:commandButton
value="Checkout"
action="#{shopBean.checkout}" />
</h:form>
The Bean
@Named
@SessionScoped
public class ShopBean implements Serializable {
// Value Change Listener
public void catChanged(
AjaxBehaviorEvent event) {
loadProducts(category);
}
// Action Listener
public void addToCart(ActionEvent e) {
cart.add(selectedProduct);
}
// Programmatic Navigation
public String checkout() {
if (cart.isEmpty()) {
return null; // Stay on page
}
return "checkout"; // Navigate
}
}
🚀 Quick Summary
| Concept | What It Does | Code Hint |
|---|---|---|
| Implicit Nav | Auto-navigate by name | action="pageName" |
| Programmatic Nav | Code decides where | return "pageName"; |
| Action Event | Button click handler | actionListener= |
| Value Change | Input change handler | valueChangeListener= |
| Phase Event | Lifecycle hooks | PhaseListener |
💡 Remember!
- Implicit = Easy, just use the page name
- Programmatic = Smart, your code decides
- Action Events = Clicks
- Value Change = Typing/selecting
- Phase Events = Behind the scenes
You’re now ready to navigate your Jakarta EE magic mall like a pro! 🎉
