How to create a reusable view using JSF

Here is the scenario - I want to create a view that can be used from several other views to create and edit a specific type of object.

My application has an address object that can be shared by other objects. In the view that supports the object, I would like the button / link to go into the address editing view for the address associated with this object. Another view that another object is processing should also be able to go to the edit address view with its address. After that, editing the address will be returned to the caller when editing is completed.

My problem is that I cannot find a way to pass the address object from the first view to the address view.

I think I need some area of โ€‹โ€‹conversation, but I donโ€™t know how to get the address without knowing the bean page that links to it, but obviously my address view can only know about addresses.

I use JSF2.1 (MyFaces / PrimeFaces) and CDI (OpenWebBeans) and CODI.

I am sure that I should miss something simple. (Simple regarding the JSF / CDI members that are!)

+4
source share
3 answers

I think I came up with a solution.

Since I use CODI, I can use the ConversationGroup annotation. I created the emtpy AddressConversation interface and then added it to all the beans support, which should show the address/addressEdit.xhtml , as well as the bean support for the addressEdit .

I also use CODI view configuration, so my action methods return ViewConfig derived class objects.

 @Named @ConversationScoped @ConversationGroup(AddressConversation.class) public class AddressView implements Serializable { private Class<? extends Views> fromView; private Address editAddress; private Address returnAddress; // Getters/setters etc... public Class<? extends Views> cancelEdit() { returnAddress = null; return fromView; } } 

So, in my calling view (PrimeFaces commandLink)

 <p:commandLink value="#{enquiryView.addressLinkText}" action="#{enquiryView.editAddress()}" immediate="true"/> 

and in a backup bean EnquiryView I can @Inject create an instance of AddressView in the correct conversation group, and then set the address and return properties when the action method is called.

 @Named @ConversationScoped @ConversationGroup(AddressConversation.class) public class EnquiryView implements Serializable { @Inject @ConversationGroup(AddressConversation.class) private AddressView addrView; public Class<? extends Views> editAddress() { addrView.setAddress(editEnq.getAddress()); addrView.setFromView(Views.Enquiry.EnquiryEdit.class); return Views.Address.AddressEdit.class; } } 

I can also watch navigation in the EnquiryView and update the query object when the address has been saved as an address edit.

 protected void onViewConfigNav(@Observes PreViewConfigNavigateEvent navigateEvent) { if (navigateEvent.getFromView() == Views.Address.AddressEdit.class && navigateEvent.getToView() == Views.Enquiry.EnquiryEdit.class) { onEditAddressReturn(); } } private void onEditAddressReturn() { if (addrView.getReturnAddress() != null) { // Save pressed editEnq.setAddress(addrView.getReturnAddress()); } } 
+2
source

Just pass the address identifier as the request parameter and specify the target view to convert, validate and set it to bean on <f:viewParam> .

eg.

 <h:link value="Edit address" outcome="addresses/edit"> <f:param name="id" value="#{address.id}" /> </h:link> 

and then in addresses/edit.xhtml

 <f:metadata> <f:viewParam id="id" name="id" value="#{editAddressBacking.address}" converter="#{addressConverter}" converterMessage="Bad request. Unknown address." required="true" requiredMessage="Bad request. Please use a link from within the system." /> </f:metadata> <h:message for="id" /> <h:form rendered="#{not empty editAddressBacking.address}"> <h:inputText value="#{editAddressBacking.address.street}" /> ... </h:form> 

To return to the original page, you can pass another request parameter.

 <h:link value="Edit address" outcome="addresses/edit"> <f:param name="id" value="#{address.id}" /> <f:param name="from" value="#{view.viewId}" /> </h:link> 

(where #{view} is the implicit object related to the current UIViewRoot )

and set it the same as <f:viewParam> so that you can just return to it in the bean edit address sending mode:

 public String save() { // ... return from + "?faces-redirect=true"; } 

See also:

+1
source

If you want another object to be set when the address is ok, just let the addresses handle the EL bean name you want to set:

 <f:param name="targetBeanSetter" value="enquiryBean.adress" /> 

And in Java:

 public String executeAndBack() { int last = this.targetBeanSetter.lastIndexOf('.'); String base = this.targetBeanSetter.substring(0, last); String property = this.targetBeanSetter.substring(last + 1); Object yourEntityToSet = FacesContext.getCurrentInstance().getELContext().getELResolver().getValue(context.getELContext(), null, base); try { PropertyUtils.setSimpleProperty(yourEntityToSet, property, constructeurHolder.getConstructeur()); } catch (Throwable e) { throw new RuntimeException(e.getMessage()); } return from + "?faces-redirect=true"; } 

If you only need access to the selected address, without creating related objects, when you return to the first page, simply enter into the InquiryView using

 @ManagedProperty(value="{address}") Address adress; 
0
source

Source: https://habr.com/ru/post/1390893/


All Articles