For a quick overview of Mastering OmniFaces book, please check out the SlideShare presentation below:
|
|
Search on blog
Petition by Java EE Guardians
joi, 28 ianuarie 2016
marți, 26 ianuarie 2016
[OmniFaces utilities (2.3)] - Check if the given CDI managed bean scope is active
[OmniFaces utilities] The
isActive()
returns true
when the given CDI managed bean scope is active. I.e., all beans therein can be accessed without facing ContextNotActiveException
.Method:
For example, you can test if the request scope is active like this:
import
org.omnifaces.util.Beans;
...
boolean
isrequestscopeactive = Beans.isActive(RequestScoped.class);
if (isrequestscopeactive){
// do something
} else {
// do something else
}
luni, 25 ianuarie 2016
PrimeFaces cross-field client bean validation sample
As
you probably know, PrimeFaces comes with a very useful support for client side
validation based on JSF validation API and Bean Validation. In this post we
will focus on Bean Validation, and say that this can be successfully used as
long as we don't need cross-field validation or class level validation. This
means that the validation constrains placed at class level will not be
recognized by PrimeFaces client side validation.
In
this post, you can see a pretty custom solution, but pretty fast to implement in order to obtain a cross-field client side validation for Bean Validation using PrimeFaces. We
have a user contact made of a name and an e-mail, and our validation constraint
is of type: e-mail must start with name (e.g. name@domain.com):
<h:form>
<p:panelGrid columns="3">
<p:outputLabel for="nameId"
value="Name"/>
<p:inputText id="nameId"
value="#{contactBean.name}"/>
<p:message for="nameId"/>
<p:outputLabel for="emailId"
value="E-mail"/>
<p:inputText id="emailId"
value="#{contactBean.email}"/>
<p:message
for="emailId"/>
<p:commandButton value="Contact
Member" action="#{contactBean.someAction()}"
update="@form"
validateClient="true"/>
</p:panelGrid>
<p:messages/>
</h:form>
For
accomplishing this task, we will slightly adapt the PrimeFaces custom client
side validation.
First,
we create ValidContact annotation:
@Documented
@Constraint(validatedBy
= {ContactValidator.class})
@ClientConstraint(resolvedBy=ValidContactClientConstraint.class)
@Target({ANNOTATION_TYPE,
METHOD, FIELD})
@Retention(RUNTIME)
public
@interface ValidContact {
String message() default "Invalid contact
!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload()
default {};
}
Further,
in our bean we annotate the proper fields (name and email) with this annotation - we need to do this to
indicate the fields that enters in cross-field validation; so, annotate each
such field:
@Named
@RequestScoped
public class
ContactBean implements Serializable {
private static final long serialVersionUID =
1L;
@ValidContact(message = "The name should
be used in e-mail as name@domain.com!")
private String name;
@ValidContact(message = "The e-mail
should be of type name@domain.com!")
private String email;
// getters and setters
public void someAction() {
Messages.addGlobalInfo("Thank you for
your contacts!");
}
}
Now,
we write the validator. Here, we need to keep the name until the validator gets
the e-mail also. For this, we can use the faces context attributes, as below:
public class
ContactValidator implements ConstraintValidator<ValidContact, String> {
@Override
public void initialize(ValidContact
constraintAnnotation) {
// NOOP
}
@Override
public boolean isValid(String value,
ConstraintValidatorContext context) {
if
(Faces.getContextAttribute("NAME_VALUE") == null) {
Faces.setContextAttribute("NAME_VALUE",
value);
} else
{
return
value.startsWith(String.valueOf(Faces.getContextAttribute("NAME_VALUE")));
}
return true;
}
}
Now,
we have to accomplish the client-side validation. Again, notice that we store
the name into an array (you can add here more fields) and wait for the e-mail:
<script
type="text/javascript">
var data = [];
PrimeFaces.validator['ValidContact'] = {
MESSAGE_ID:
'org.primefaces.examples.validate.contact.message',
validate:
function (element, value) {
if (data.length == 0) {
data.push(value);
} else {
if (!value.startsWith(data[0])) {
var msgStr =
element.data('p-contact-msg'),
msg = msgStr ? {summary: msgStr, detail:
msgStr} :
vc.getMessage(this.MESSAGE_ID);
throw msg;
}
}
}
};
</script>
Finally,
we ensure the presence of ClientValidationConstraint
implementation:
public class
ValidContactClientConstraint implements ClientValidationConstraint {
public static final String MESSAGE_METADATA =
"data-p-contact-msg";
public Map<String, Object>
getMetadata(ConstraintDescriptor constraintDescriptor) {
Map<String, Object> metadata = new
HashMap<String, Object>();
Map attrs =
constraintDescriptor.getAttributes();
Object message =
attrs.get("message");
if (message != null) {
metadata.put(MESSAGE_METADATA, message);
}
return metadata;
}
public String getValidatorId() {
return
ValidContact.class.getSimpleName();
}
}
Valid screenshot:
Invalid screenshot:
Done!
The complete application is available here.
Read more such goodies in:
Read more such goodies in:
PrimeFaces & OmniFaces - Powers Combined
duminică, 24 ianuarie 2016
PrimeFaces ring with OmniFaces unique value column
As you probably know, PrimeFaces comes with a component capable to display data with a circular animation. This component is named Ring.
For example, the below snippet of code will work in conjunction with BarcelonaSquadBean to return the result from image below:
For example, the below snippet of code will work in conjunction with BarcelonaSquadBean to return the result from image below:
<h:form
id="form">
<p:panel>
<p:ring id="custom"
value="#{barcelonaSquadBean.players}" var="t">
<p:outputPanel style="text-align:
center; height:90%;" layout="block">
<p:inputText value="#{t.name}" required="true"
style="width: 90%; height: inherit;"/>
</p:outputPanel>
</p:ring>
</p:panel>
<p:panel style="text-align: center;
margin-top: 10px;">
<p:commandButton
actionListener="#{barcelonaSquadBean.someAction()}"
update="@form"
value="Subscribe
Team"/>
<p:messages id="msgs"/>
</p:panel>
</h:form>
The issue consist in the fact that we can mistakenly provide the same player name twice and obviosuly this is not diserable. In order to fix this, we can use the OmniFaces ValidateUniqueColumn validator. This validator is exposed to page authors via <o:validateUniqueColumn/> tag. The main characteristics of this validator are:
- It validates if the given UIInput component in an UIData component has an unique value throughout all rows.
- It includes in validation the data that is not visible in case of using pagination, as long as it is in the data model.
- It works directly on the data model (e.g. when lazy loading is used, the data model contains a subset of data; only this subset counts on validation).
- It signals only the first invalid row on which the value is actually changed.
- A faces message will be added on the client ID of the input component in this invalid row.
- Its default message is of type (the {0} placeholder represents the label of the input component, while the {1} placeholder represents the 1‐based row index of the data model):
{0}: Please fill out an unique value for the entire
column. Duplicate found in row {1}
If you need to a custom message then just use the message attribute:
<o:validateUniqueColumn message="custom
message here" />
So, we can use <o:validateUniqueColumn/> in our case like below:
<p:panel>
<p:ring id="custom"
value="#{barcelonaSquadBean.players}" var="t">
<p:outputPanel style="text-align:
center; height:90%;" layout="block">
<p:inputText value="#{t.name}"
required="true" style="width: 90%; height:
inherit;">
<o:validateUniqueColumn message="Duplicate player not
allowed!"/>
</p:inputText>
</p:outputPanel>
</p:ring>
</p:panel>
This time we cannot submit the team with duplicate players. Check
figure below:
The complete application is available here.
Read more such goodies in:
Read more such goodies in:
PrimeFaces & OmniFaces - Powers Combined
vineri, 22 ianuarie 2016
Combining @ListenerFor with SystemEventListener
Conforming to documentation, "if
the class to which @ListenerFor annotation is attached implements SystemEventListener and does not implement ComponentSystemEventListener, "target" is the Application instance".
Based on this affirmation, it is possible to believe that the below
example will work and will register the TomComponent as a listener for all emitters
capable to emit the PostAddToViewEvent
event (not just instances of TomComponent):
@FacesComponent(value
= TomComponent.COMPONENT_TYPE, createTag = true)
@ListenerFor(systemEventClass =
PostAddToViewEvent.class)
public class
TomComponent extends UIComponentBase implements SystemEventListener {
public static final String COMPONENT_FAMILY =
"jsf.component";
public static final String COMPONENT_TYPE = "jsf.component.TomComponent";
@Override
public
void processEvent(SystemEvent event) throws AbortProcessingException {
System.out.println("EVENT EMITTED:
" + event);
}
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}
@Override
public
boolean isListenerForSource(Object source) {
System.out.println("EVENT SOURCE: "
+ source);
return true;
}
...
}
But, remember that an custom component implements the ComponentSystemEventListener
interface by inheritance from UIComponent!
So, the output of the above case will be:
EVENT SOURCE:
jsf.component.TomComponent
And the processEvent(SystemEvent
event) is not called! Since ComponentSystemEventListener is inherited (so
implemented), and we have the @ListenerFor,
the TomComponent
will be registered as the listener for PostAddToViewEvent event emitted only by instances of TomComponent. But, as
you notice from the above output, the flow passes through the isListenerForSource(Object
source), which means that, in this particular case, we need to return true;, or return source instanceof
TomComponent;, otherwise we will block the call of the processEvent(ComponentSystemEventListener
event) method.
So, combining @ListenerFor
with SystemEventListener
and UIComponents
is not a good thing, only if you really know what you are doing.
Well, the things changes in case of combining the @ListenerFor with SystemEventListener and Renderer. Since Renderer doesn't implement the ComponentSystemEventListener, we are in
the case from the documentation quoted above. Now, the reasoning is going
further and "If "target"
is the Application instance, inspect the value of
the sourceClass() annotation attribute value. If the value is Void.class, call Application.subscribeToEvent(Class,
SystemEventListener), passing the value of systemEventClass()
as the first argument and the instance of the class to which this annotation is
attached (which must implement SystemEventListener)
as the second argument".
miercuri, 20 ianuarie 2016
Using PrimeFaces and OmniFaces to customize the PrimeFaces client-side bean validation error messages
As you probably know, PrimeFaces support Bean Validation. Actually,
PrimeFaces comes with a client side
validation framework integrated with Bean Validation Specification. Basically, PrimeFaces obtains via a set of
classes (located in org.primefaces.validate.bean)
the server side validation constraints for each type of supported validators.
These constrains will be used in a JavaScript file named beanvalidation.js to
perform the validation on client side and generate the corresponding messages.
For example, let's suppose that we have the following bean:
@Named
@RequestScoped
public class
DataBean {
@Size(min = 2, max = 25)
private String name;
@Size(min = 2, max = 10)
private String surname;
// getters and setters
}
PrimeFaces extracts the constraints for @Size via org.primefaces.validate.bean.SizeClientValidationConstraint,
like below:
package
org.primefaces.validate.bean;
import
java.util.HashMap;
import
java.util.Map;
import javax.validation.constraints.Size;
import
javax.validation.metadata.ConstraintDescriptor;
import
org.primefaces.util.HTML;
public class
SizeClientValidationConstraint implements ClientValidationConstraint {
private static final String MESSAGE_METADATA =
"data-p-size-msg";
private static final String MESSAGE_ID = "{javax.validation.constraints.Size.message}";
public Map<String, Object>
getMetadata(ConstraintDescriptor constraintDescriptor) {
Map<String, Object> metadata = new
HashMap<String, Object>();
Map attrs =
constraintDescriptor.getAttributes();
Object message =
attrs.get("message");
metadata.put(HTML.VALIDATION_METADATA.MIN_LENGTH,
attrs.get("min"));
metadata.put(HTML.VALIDATION_METADATA.MAX_LENGTH,
attrs.get("max"));
if (!message.equals(MESSAGE_ID)) {
metadata.put(MESSAGE_METADATA, message);
}
return metadata;
}
public String getValidatorId() {
return Size.class.getSimpleName();
}
}
Now, the extracted constrains are used in beanvalidation.js, as below:
if
(window.PrimeFaces) {
...
PrimeFaces.locales.en_US.messages["javax.validation.constraints.Size.message"]
= "size must be between {0} and {1}";
PrimeFaces.validator.Size = {
MESSAGE_ID:
"javax.validation.constraints.Size.message",
validate: function(d, f) {
if (f !== null) {
var e = d.val().length,
c = d.data("p-minlength"),
a = d.data("p-maxlength"),
b =
PrimeFaces.util.ValidationContext;
if (e < c || e > a) {
throw b.getMessageBV(d, this.MESSAGE_ID,
d.data("p-size-msg"), c, a)
}
}
}
};
...
};
So, this will produce the error messages from figure below:
Now, let's suppose that we want to change this message and involve more information in it. For example, let's suppose that we want to provide in the error messages the labels of the invalid components. So, instead of the above image, we want to obtain something like this:
Note In case of server-side validation via Bean Validation, you can accomplish this task via OmniFaces JsfLabelMessageInterpolator.
In order to accomplish this task we have several approaches. One of
these approaches consist in overriding the SizeClientValidationConstraint for extracting
the validated input label and overriding the client-side implementation to take
this label into account.
So, first we will write a new SizeClientValidationConstraint like below:
import
java.util.HashMap;
import
java.util.Map;
import
javax.validation.constraints.Size;
import
javax.validation.metadata.ConstraintDescriptor;
import static
org.omnifaces.util.Components.getCurrentComponent;
import static
org.omnifaces.util.Components.getLabel;
import
org.primefaces.util.HTML;
public class
SizeClientValidationConstraint implements ClientValidationConstraint {
private static final String MESSAGE_METADATA =
"data-p-size-msg";
private static final String MESSAGE_ID =
"{javax.validation.constraints.Size.message}";
public Map<String, Object>
getMetadata(ConstraintDescriptor constraintDescriptor) {
Map<String,
Object> metadata = new HashMap<String, Object>();
Map
attrs = constraintDescriptor.getAttributes();
Object
message = attrs.get("message");
metadata.put(HTML.VALIDATION_METADATA.MIN_LENGTH,
attrs.get("min"));
metadata.put(HTML.VALIDATION_METADATA.MAX_LENGTH,
attrs.get("max"));
metadata.put(HTML.VALIDATION_METADATA.LABEL,
getLabel(getCurrentComponent()));
if (!message.equals(MESSAGE_ID))
{
metadata.put(MESSAGE_METADATA, message);
}
return
metadata;
}
public String getValidatorId() {
return Size.class.getSimpleName();
}
}
Thanks to OmniFaces utilities, we can obtain the label of the current
validated input component via Components#getCurrentComponent()
and Components#getLabel().
This save us for writing a good chunk of code! Further, we simply add the label
in the metadata under HTML.VALIDATION_METADATA.LABEL.
Now, on client-side, we adjust the JavaScript code to use this label in
error validation messages for @Size:
<script
type="text/javascript">
//<![CDATA[
PrimeFaces.locales.en_US.messages["javax.validation.constraints.Size.message"]
= "The size of {0} must be between {1} and {2} characters";
PrimeFaces.validator.Size = {
MESSAGE_ID:
"javax.validation.constraints.Size.message",
validate: function (d, f) {
if (f !== null) {
var e = d.val().length,
c = d.data("p-minlength"),
a = d.data("p-maxlength"),
b =
PrimeFaces.util.ValidationContext;
if (e < c || e > a) {
throw b.getMessageBV(d, this.MESSAGE_ID,
d.data("p-size-msg"), l, c, a)
}
}
}
};
//]]>
</script>
Done! The complete application in available here.
Read more such goodies in:
Read more such goodies in:
PrimeFaces & OmniFaces - Powers Combined
marți, 19 ianuarie 2016
Use a JSF built-in converter to preserve data type
Let's suppose that we have a <h:selectManyListbox> used in the classical way:
<h:form>
<h:selectManyListbox value="#{playerBean.selectedRanks}">
<f:selectItems
value="#{playerBean.playersRanks}"/>
</h:selectManyListbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
Basically, the playersRanks
is a list of integers that "populates" our list, and the selectedRanks
represents the user selections:
@Named
@SessionScoped
public class
PlayerBean implements Serializable {
private ArrayList<Integer>
selectedRanks;
private static final ArrayList<Integer>
playersRanks;
static {
playersRanks = new ArrayList<>();
playersRanks.add(1);
playersRanks.add(2);
playersRanks.add(3);
}
public ArrayList<Integer>
getPlayersRanks() {
return
playersRanks;
}
public ArrayList<Integer>
getSelectedRanks() {
return
selectedRanks;
}
public void
setSelectedRanks(ArrayList<Integer> selectedRanks) {
this.selectedRanks = selectedRanks;
}
public String selectedAction() {
// TODO
}
}
So, the user may select the ranks and submit them without issues/errors.
Even if no error occurred, we can notice a "strange" behavior if we
try to run the following snippet of code:
<ui:repeat
value="#{playerBean.selectedRanks}" var="i">
#{i}: #{i.getClass()}
</ui:repeat>
The output reveals that the selected ranks are strings, not integers as
we expected to see:
1: class
java.lang.String
3: class
java.lang.String
The explanation relies on the fact that "the generic type information of List<Integer> is
lost during runtime and therefore JSF/EL who sees only List is
not able to identify that the generic type is Integer and assumes it to be
default String (as that's the default type of the
underlying HttpServletRequest#getParameter() call
during apply request values phase) " - Bauke Scholtz (aka BalusC).
There are two approaches:
·
explicitly specify a Converter
·
use Integer[] instead
In this case, we can use the built-in javax.faces.Integer built-in converter:
<h:form>
<h:selectManyListbox
value="#{playerBean.selectedRanks}"
converter="javax.faces.Integer">
<f:selectItems
value="#{playerBean.playersRanks}"/>
</h:selectManyListbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
Now, we can perform the same test and the output will be:
1: class
java.lang.Integer
3: class
java.lang.Integer
Read further explanations here.
Abonați-vă la:
Postări
(
Atom
)
|
[JSF Page Author Beginner's Guide] |
Postări populare
-
If you like this article, I think you are going to like JSF 2.3 Tutorial as well.
-
Starting with JSF 2.3 the JSF "native" managed bean annotations are officially deprecated. So, there is no doubt now that CDI is ...
-
[OmniFaces utilities] The getRequestURL() method returns the HTTP request URL with query string, regardless of any forward. This is the f...
-
Let's checkout a common practice for declaring constants in Java using the public static final declaration: public class Circle { ...
-
[OmniFaces utilities] The getActionExpressionsAndListeners() method returns a list of all action expressions and listeners associated wit...
-
[OmniFaces utilities] The getCurrentActionSource() method returns the source of the currently invoked action, or null if there is none, ...
-
Before you read this post please read: Use a CDI alternative as a mock implementation for a stateless session bean So, since you are fa...
-
Starting with JSF 2.3, more exactly with m07 , we can take advantage of using the auto detection of convertors based on 1st UISelectMany it...
-
[OmniFaces utilities] The isSerializable() method returns true if the given object is serializable. Method: Usage: Example 1 - te...
-
[OmniFaces utilities] The reverseArray() function returns a copy of the array with items in reversed order. Function: Usage: Let...
OmniFaces/JSF Fans
-
▼
2016
(
96
)
-
▼
ianuarie
(
15
)
- Mastering OmniFaces is on SlideShare
- [OmniFaces utilities (2.3)] - Check if the given C...
- PrimeFaces cross-field client bean validation sample
- PrimeFaces ring with OmniFaces unique value column
- Combining @ListenerFor with SystemEventListener
- Using PrimeFaces and OmniFaces to customize the Pr...
- Use a JSF built-in converter to preserve data type
- Misunderstanding the JSF lifecycle phases with th...
- JSF 2.2 [usage pitfall] - Dependency injection was...
- Open the targeted view in a new browser tab
- JSF 2.2 [usage pitfall] - Prevent unintuitive beha...
- PrimeFaces & OmniFaces Powers Combined - first ti...
- JSF 2.2 [usage pitfall] - Counting the children of...
- JSF 2.2 [usage pitfall] - Resource library meaning
- [OmniFaces utilities (2.3)] Fire the given CDI eve...
-
▼
ianuarie
(
15
)
-
►
2015
(
516
)
- ► septembrie ( 38 )
JSF/OmniFaces Resources |