Evaluating if MethodExpression attribute is set in composite component (getting PropertyNotFoundException)

2 min read 07-10-2024
Evaluating if MethodExpression attribute is set in composite component (getting PropertyNotFoundException)


Navigating the PropertyNotFoundException: Evaluating MethodExpressions in Composite Components

Composite components in JSF offer a powerful way to encapsulate UI elements and logic, enhancing code reusability and maintainability. However, developers often encounter the dreaded PropertyNotFoundException when attempting to evaluate a MethodExpression within a composite component. This error arises when the component cannot locate the specified method or property.

Let's break down the situation and provide a clear solution.

Scenario: The PropertyNotFoundException

Imagine you're building a reusable RatingComponent to display and update a rating value. The component uses a MethodExpression to bind the rating value to a backing bean method:

// RatingComponent.xhtml
<h:inputText value="#{ratingBean.rating}"/>
<h:commandButton value="Submit" action="#{ratingBean.submitRating}"/> 

Now, when you use the RatingComponent in a different JSF page, you might see the following error:

javax.el.PropertyNotFoundException: Target Unreachable, identifier 'ratingBean' resolved to null

This PropertyNotFoundException arises because the ratingBean is not accessible within the composite component's scope.

The Root Cause: Scope Mismatch

The core issue lies in the way JSF handles scopes and method expressions within composite components. By default, a composite component creates its own scope, isolated from the scope of the page where it's used. This means that references like #{ratingBean} inside the component's template are evaluated within the component's scope, not the page's scope.

The Solution: Injecting Dependencies

To correctly access the ratingBean in your composite component, we need to inject it using a MethodExpression that points to the page's scope:

// RatingComponent.xhtml
<cc:interface>
  <cc:attribute name="ratingBean" required="true" type="java.lang.Object"/>
</cc:interface>

<h:inputText value="#{cc.attrs.ratingBean.rating}"/>
<h:commandButton value="Submit" action="#{cc.attrs.ratingBean.submitRating}"/>

Here's what's happening:

  1. We declare an attribute named ratingBean in the composite component's interface, marking it as required. This ensures that the page providing the component must supply a value for ratingBean.
  2. In the template, we use cc.attrs.ratingBean to access the injected ratingBean object. This expression retrieves the attribute value passed from the page, allowing us to access its properties and methods.

Understanding the cc.attrs Object

The cc.attrs object serves as a bridge between the composite component's internal scope and the page's scope. It provides access to all the attributes passed to the component. This allows you to share data and logic between the component and the page without introducing global dependencies.

Key Takeaways

  • Scope Awareness: Understanding the difference between composite component scope and page scope is crucial to avoiding PropertyNotFoundExceptions.
  • Injecting Dependencies: Use cc.attrs to access properties and methods provided by the page when working with MethodExpressions inside composite components.
  • Component Reusability: By following these best practices, you can create robust and reusable composite components that seamlessly integrate into your JSF applications.

Additional Resources

By understanding the nuances of scope and dependency injection, you can overcome the PropertyNotFoundException and craft powerful, reusable composite components.