I would like to get outcome value from navigation rules into a request scoped JSF 2 bean. How can I do that?
For example, when I press on a <h:link outcome="contacts"> and end up in the contacts page, then I would like to get the outcome "contacts" in the backing bean associated with the navigation menu.
faces-config.xml
<navigation-rule>
...
<navigation-case>
<from-outcome>contacts</from-outcome>
<to-view-id>/pages/contacts.xhtml</to-view-id>
</navigation-case>
...
</navigation-rule>
In JSF, AFAIK, only the ConfigurableNavigationHandler will have that information. So create a custom ConfigurableNavigationHandler that will stash the outcome in a request parameter for your consumption in the destination page.
Your custom navigation handler
public class NavigationHandlerTest extends ConfigurableNavigationHandler {
private NavigationHandlerTest concreteHandler;
public NavigationHandlerTest(NavigationHandler concreteHandler) {
this.concreteHandler = concreteHandler;
}
#Override
public void handleNavigation(FacesContext context, String fromAction, String outcome){
//Grab a hold of the request parameter part and save the outcome in it for
//later retrieval
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext ctx = context.getExternalContext();
ctx.getRequestMap().put("currentOutcome", outcome);
//resume normal navigation
concreteHandler.handleNavigation(context, fromAction, outcome);
}
}
Configure your handler in faces-config.xml
<application>
<navigation-handler>com.foo.bar.NavigationHandlerTest</navigation-handler>
</application>
Retrieve in your destination bean
#ManagedProperty(value="#{param.currentOutcome}")
String outcome;
//getter and setter
Related
I have started learning JSF, but sadly most tutorials out there present only a log in or a register section.
Can you point me to some more in depth examples? One thing I'm interested in is a page presenting a list of products. I'm on page home and I press on page products so that I can see the latest products added. And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
One way to solve this would be to create a session scoped managed bean in which I would place different entities updated through other managed beans. I found this kind of approach in some tutorials, but it seems quite difficult and clumsy.
Which would be the best approach to solve a thing like this? What is the correct usage of session scope in two-page master-detail user interface?
What is the correct usage of session scope
Use it for session scoped data only, nothing else. For example, the logged-in user, its settings, the chosen language, etcetera.
See also:
How to choose the right bean scope?
And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
Typically you use the request or view scope for it. Loading of the list should happen in a #PostConstruct method. If the page doesn't contain any <h:form>, then the request scope is fine. A view scoped bean would behave like a request scoped when there's no <h:form> anyway.
All "view product" and "edit product" links/buttons which just retrieve information (i.e. idempotent) whould be just plain GET <h:link> / <h:button> wherein you pass the entity identifier as a request parameter by <f:param>.
All "delete product" and "save product" links/buttons which will manipulate information (i.e. non-idempotent) should perform POST by <h:commandLink>/<h:commandButton> (you don't want them to be bookmarkable/searchbot-indexable!). This in turn requires a <h:form>. In order to preserve the data for validations and ajax requests (so that you don't need to reload/preinitialize the entity on every request), the bean should preferably be view scoped.
Note that you should basically have a separate bean for each view and also note that those beans doesn't necessarily need to reference each other.
So, given this "product" entity:
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
// ...
}
And this "product service" EJB:
#Stateless
public class ProductService {
#PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
You can have this "view products" on /products.xhtml:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
#Named
#RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
#EJB
private ProductService productService;
#PostConstruct
public void init() {
products = productService.list();
}
// ...
}
And you can have this "edit product" on /products/edit.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
#Named
#ViewScoped
public class EditProduct {
private Product product; // +getter +setter
#EJB
private ProductService productService;
public String save() {
productService.update(product);
return "/products?faces-redirect=true";
}
// ...
}
And this converter for <f:viewParam> of "edit product":
#Named
#RequestScoped
public class ProductConverter implements Converter {
#EJB
private ProductService productService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
You can even use a generic converter, this is explained in Implement converters for entities with Java Generics.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
JSF Controller, Service and DAO
JSF Service Layer
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Communication in JSF 2.0 - Contains several examples/hints
As a small improvement to what BalusC recommended, sometimes you can remove the required / requiredMessage part from the <f:viewParam> of your "details" screen and instead use the conditional rendering of the editing form (as BalusC did) with a reverse condition for recommending a specific link for the "list/master" screen or, even use a viewAction that would test the param and force a redirect to that list.
I need to render an html table having precise control over rows, columns, headers, styles etc.
I'm using a primeFaces panelGrid this way:
<p:panelGrid binding="#{myBean.tableComponent}"/>
In my backing bean, I have:
private UIComponent tableComponent;
public UIComponent getTableComponent() {
if (tableComponent == null) {
tableComponent = new PanelGrid();
populateTableComponent(); // Populate datatable.
}
return tableComponent;
}
public void setTableComponent(UIComponent tableComponent) {
this.tableComponent = tableComponent;
}
private void populateTableComponent() {
PanelGrid tbl = (PanelGrid) tableComponent;
//...
for (MyPojo row : data.getRows) {
// ...here I create the row/column UIComponent subtree
}
}
Now, my problem is:
For a specific column, I have to render a commandLink in each row.
This Link should AJAX-call a bean's method which should do something related to the clicked row.
Something like <p:commandLink action="#{myBean.myFieldClick(***row***)}"> but
how can I refer to row?
Other ideas?
Thank you in advance
Just use exactly the same variable name as definied in var attribute of the data table. In other words, write down exactly the same EL expression string as you would do when normally writing it in a view file instead of in a backing bean.
Thus, the action attribute of the following command link example in the view
<p:dataTable ... var="row">
...
<p:commandLink ... action="#{myBean.myFieldClick(row)}">
could programmatically be represented as
MethodExpression action = createMethodExpression("#{myBean.myFieldClick(row)}", null, Row.class);
with this helper method
public static MethodExpression createMethodExpression(String expression, Class<?> returnType, Class<?>... parameterTypes) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createMethodExpression(
facesContext.getELContext(), expression, returnType, parameterTypes);
}
how to get logged in user id in jsf 2.0
I am unable to get the user id. I tried the following methods.
xhtml page
<h:outputText value="#{request.remoteUser}"></h:outputText>
<h:outputText value="#{userDetails.userId}"></h:outputText>
Viewscoped bean class.
public String getUserId() {
FacesContext ctx = FacesContext.getCurrentInstance();
System.out.println ("User"+ctx.getExternalContext().getRemoteUser());
userId =ctx.getExternalContext().getRemoteUser();
return userId;
}
Should i do any initialization in web.xml? or should my bean class extend or implement any other class?
I cant seem to get the view scoped managed bean to work with setPropertyActionListener:
<h:commandButton value="Edit" action="edit-company.xhtml">
<f:setPropertyActionListener target="#{companyHolder.item}" value="#{company}"/>
</h:commandButton>
This works fine if companyHolder is session or request scoped but doesnt work if its view scoped. Is this normal?
A brand new view scoped bean is been created when a new view is created. The target view holds a different instance of the view scoped bean than where the property is been set by the action method on the initial view with the form.
This is at first sight indeed unintuitive, but that's how the view scope works. A view scoped bean lives as long as the view lives. It makes sense after all.
Your best bet is using <f:param> instead of <f:setPropertyActionListener> and let the target view set it by <f:viewParam>.
E.g.
<h:commandButton value="Edit" action="edit-company.xhtml">
<f:param name="companyId" value="#{company.id}"/>
</h:commandButton>
with
<f:metadata>
<f:viewParam name="companyId" value="#{bean.company}" required="true" />
</f:metadata>
and
#ManagedBean
#ViewScoped
public class Bean {
private Company company;
// ...
}
and
#FacesConverter(forClass=Company.class)
public class CompanyConverter implements Converter {
#Override
public void getAsObject(FacesContext context, UIComponent component, Object value) throws ConverterException {
try {
return companyService.find(Long.valueOf(value));
} catch (Exception e) {
throw new ConverterException(new FacesMessage(
String.format("Cannot convert %s to Company", value)), e);
}
}
// ...
}
As a completely different alternative, you can also just navigate back to the same view by returning void or null and render the include conditionally.
<ui:include src="#{bean.editmode ? 'edit' : 'view'}.xhtml" />
This however doesn't work if you require to support GET instead of POST (for which you would need to replace <h:commandButton> by <h:button> by the way).
I am using jsf 2.0 and I have two bean Navigation (Application Scope ) and Module (Request Scope). I want to use methods of Navigation bean in Module Bean. I am doing in this way
In Module Bean
#ManagedProperty(value = "#{navigationBean}")
private NavigationBean navigationBean;
But when I am trying to get navigationBean.SomeMethod it is not working as navigation bean is null . How to do this?
The both beans needs to be a fullworthy #ManagedBean. The acceptor should have a public setter method for the injected bean. The injected bean is only available in #PostConstruct and beyond (i.e. in all normal event methods, but thus not in the constructor of the acceptor).
So, this ought to work:
#ManagedBean
#ApplicationScoped
public class Navigation {
// ...
}
#ManagedBean
#RequestScoped
public class Module {
#ManagedProperty(value="#{navigation}")
private Navigation navigation;
#PostConstruct
public void init() {
navigation.doSomething();
}
public void setNavigation(Navigation navigation) {
this.navigation = navigation;
}
}
I think #ManagedProperty requires a public set method to work.
I got The solution
I have a method in application signature boolean getReadAccess(String role, String module ). If i want to use in another bean then i have to follow these steps
`javax.el.MethodExpression readAccess;
javax.el.ELContext elContext = null;
javax.faces.context.FacesContext context = FacesContext.getCurrentInstance();
elContext = ((FacesContext) context).getELContext();
javax.faces.application.Application application = FacesContext.getCurrentInstance().getApplication();
javax.el.ExpressionFactory expressionFactory = application.getExpressionFactory();
readAccess = expressionFactory.createMethodExpression(elContext,
"#{navigationBean.getReadAccess}", Void.class, new Class[] {
String.class, String.class });
//--------Call----------------------------
return (Boolean) readAccess.invoke(elContext, new Object[] {
"roleName", "moduleName" });
`