So I made a composite component FileAdder.xhtml
<composite:interface>
<composite:attribute name="type" value="#{editoriCompositeController.typeString}"/>
</composite:interface>
<composite:implementation>
<h:form>
<p:editor id="editor" widgetVar="editorWidget" value="some text" width="600" />
</h:form>
</composite:implementation>
And then I have the EditoriCompositeController ManagedBean:
#ViewScoped
#ManagedBean
public class EditoriCompositeController {
String typeString;
public void setTypeString(String typeStringParameter) {
this.typeString = typeStringParameter;
}
public String getTypeString() {
return typeString;
}
}
And then in my fileattachmentsview.xhtml I use the component:
<owncomponents:fileadder type="MEMO" />
But that is not setting the typeString value in the backing bean as "MEMO". It remains as null I tested it with a button that prints the value.
How can I make the backing bean get the value for typeString I set to the composite component's type-attribute as "MEMO"? Why it's null and not "MEMO"?
You have to pass the target bean/model as another composite attribute. Then you can inside the composite use <c:set> to set a property on it.
<cc:interface>
<cc:attribute name="bean" type="com.example.Bean" />
<cc:attribute name="type" type="java.lang.String" />
</cc:interface>
<cc:implementation>
<c:set target="#{cc.attrs.bean}" property="type" value="#{cc.attrs.type}" />
<p:editor value="#{cc.attrs.bean.text}" />
</cc:implementation>
Usage:
public class Bean {
private String text;
private String type; // I suggest to make it an enum.
// ...
}
<h:form>
<your:composite bean="#{bean}" type="MEMO" />
<p:commandButton action="#{bean.submit}" />
</h:form>
Note that I factored the form outside the composite. Having a form inside a composite is poor practice.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?
I solved it by manually getting the "type" attribute from the component in the backing bean by:
String typeString = (String) component.getAttributes().get("type");
Related
I was able to see data in the datatable before adding the lazy attribute. After I added the lazy attribute, the datatable is empty, because my debug points were never reached which are inside the load method of LazyDataModel. In other words load method is not called, I see the control until this.searchResults in my search()
I just get my results from web services (which work just fine) I have looked at most of the links here and here. I made sure the lazy attribute and setRowCount are set. May be someone can help me out to figure out the problem. I am using PrimeFaces 6.0, CDI, JSF 2.2, Deltaspike 1.7.2
Here is my JSF
// other input form fields
<p:panel style="border-style : none;" styleClass="panelClass">
<h:panelGrid columns="2">
<!-- <h:commandButton action="#{search.search}" value="Search" styleClass="button_small_white" /> -->
<p:commandButton action="#{search.search}" ajax="true" update=":mainform:searchResultTable" value="Search" styleClass="button_small_white" />
<h:commandButton action="#{search.clear}" value="Clear" styleClass="button_small_white" />
</h:panelGrid>
</p:panel>
<br /><p:dataTable value="#{search.searchResults}" var="rec"
rowKey="rec.numTxt"
paginator="true" rows="10"
paginatorTemplate=" Display {RowsPerPageDropdown} Records {FirstPageLink}
{PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} "
rowsPerPageTemplate="10,25,50,100"
paginatorPosition="top"
rendered="#{not empty search.searchResults}"
id="searchResultTable"
widgetVar="searchTable"
lazy="true"
summary="RE Search Results are shown below">
<p:ajax event="page" listener="#{search.search}" update=":mainform:searchResultTable" />
<p:column headerText="">
<p:selectBooleanCheckbox value="#{rec.select}" />
</p:column>
...
</p:dataTable>
Controller
#Named("search")
#Stateful
#GroupedConversationScoped
#ConversationGroup(SearchGrp.class)
public class ReSearchController extends BaseWebServicesSearchController<ReSearchSummary>
implements ReGrp, ReSearchControllerLocal {
#Inject
private GroupedConversation conversation;
#Inject
private WindowContext windowContext;
private LazyDataModel<ReSearchSummary> searchResults;
...
#PostConstruct
#Override
public void init() {
// code for initializing my web services
search();
}
#Override
public String search(){
this.searchResults = new LazyDataModel<ReSearchSummary>() {
private static final long serialVersionUID = 4168363870903585956L;
#Override
public List<ReSearchSummary> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
List<ReSearchSummary> resultsList = null;
resultsList = search(first, pageSize);
// rowCount
setRowCount(getResultCount());
// I do not need sorting oor filters os did not use them
return resultsList;
}
};
searchResults.setRowCount(getResultCount());
return "/cr-re-search.xhtml?faces-redirect=true";
}
#Override
public ArrayList<ReSearchSummary> search(int first, int pageSize){
// this is my webservice call, this works fine if I call it indepedently
// criteria gets the form inputs
return doSearch(criteria.getForm(), pageSize, first);
}
...
}
Update: Converted h:commandButton to p:commandButton for the search button
<p:commandButton action="#{search.search}" ajax="true" update=":mainform:searchResultTable" value="Search" styleClass="button_small_white" />
And added p:ajax inside p:dataTable
<p:ajax event="page" listener="#{search.search}" update=":mainform:searchResultTable" />
I got a very funny issues: When I tried to access selectOneMenu selected value by using valueChangeListener's valueChangeEvent. I got itemLabel instead of itemValue.
jsf snappet:
<p:selectOneMenu id="position" value="#{employmentBean.currentEmployment.positionId}" required="true" effect="fold" editable="true"
requiredMessage="Please select an available option." hideNoSelectionOption="true" immediate="true" valueChangeListener="#{employmentControlBean.handlepositionIdSelected}">
<p:ajax update="#form" />
<f:selectItem itemLabel="Select Position to Create Employment." itemValue="" />
<f:selectItems value="#{positionBean.vacantPositionMap.entrySet()}" var="entry" itemLabel="#{entry.value}" itemValue="#{entry.key}"/>
</p:selectOneMenu>
my vacantPositionMap is a
Map<String,String>
style key/value pair, key is position Id, value is position Name.
my backing bean:
#Named(value = "employmentControlBean")
#SessionScoped
public class EmploymentControlBean(){
public void handlepositionIdSelected(ValueChangeEvent event){
........
String positionId = (String) event.getNewValue();
positionBean.setCurrentPosition((Position) positionBean.getPositionCache().get(positionId));
employmentBean.getCurrentEmployment().setPositionId(positionId);
}
}
from above snappet you can see. I am trying to fetch selectOneMenu selected value on valueChangeEvent.
Backing bean method by using ValueChangeEvent, I suppose JSF's itemValue will be passed to backing bean by using ValueChangeEvent.
This is what I found in my html by using firebug:
From above image you can see, primeFaces rendering html correctly, but when I tried to access selected value I get "President". It is not I expected, I expect will get "POSTN20161215xxxxxx".
My question is: Why ValueChangeEvent pass itemLabel instead of itemValue? How I can get the correct value?
Please advise!!
How can I create an "edit" button so that when the button is clicked it will change the h:outputText to h:inputText?
Make use of the rendered attribute:
<h:outputText value="#{bean.entity.property}" rendered="#{not bean.editmode}" />
<h:inputText value="#{bean.entity.property}" rendered="#{bean.editmode}" />
...
<h:commandButton value="Edit" action="#{bean.edit}" rendered="#{not bean.editmode}" />
<h:commandButton value="Save" action="#{bean.save}" rendered="#{bean.editmode}" />
With this in a view scoped bean:
private boolean editmode;
public void edit() {
editmode = true;
}
public void save() {
entityService.save(entity);
editmode = false;
}
public boolean isEditmode() {
return editmode;
}
// ...
Note that the bean being view scoped is important for the reason mentioned in point 5 of this answer: commandButton/commandLink/ajax action/listener method not invoked or input value not updated.
Alternatively, you can use the disabled attribute on input component in combination with a shot of CSS which basically makes it look like an output component (by removing the border).
<h:inputText value="#{bean.entity.property}" disabled="#{not bean.editmode}" />
...
<h:commandButton value="Edit" action="#{bean.edit}" rendered="#{not bean.editmode}" />
<h:commandButton value="Save" action="#{bean.save}" rendered="#{bean.editmode}" />
with e.g.
input[disabled] {
border: 0;
}
Also here, the bean must be view scoped. See also How to choose the right bean scope?
I'm using Primefaces 3.5 and have run into an issue with changing the ActiveIndex. ActiveIndex doesn't change when going between tabs. This is my TabMenu code:
<h:form id="formMenu">
<p:tabMenu id="tabMenu" activeIndex="#{toolbarBean.currentTab}">
<p:menuitem value="Main" action="#{toolbarBean.changeActiveIndex(0)}" />
<p:menuitem value="Page2" action="#{toolbarBean.changeActiveIndex(1)}" />
<p:menuitem value="Page3" actionListener="#{toolbarBean.changeActiveIndex(2)}" />
<p:menuitem value="Page4" action="#{toolbarBean.changeActiveIndex(3)}" />
</p:tabMenu>
</form>
toolbarBean.java
#Named
#SessionScoped
public class toolbarBean implements Serializable {
private int currentTab;
public int getCurrentTab() {
return currentTab;
}
public void setCurrentTab(int currentTab) {
this.currentTab = currentTab;
}
public String changeActiveIndex(int currentTab) {
this.currentTab = currentTab;
switch (currentTab) {
case 0:
return "/main";
case 1:
return "/page2";
case 2:
return "/page3";
case 3:
return "/page4";
default:
return "/page5";
}
}
}
The action part works properly where the variable currentTab gets assigned the right value. However, once it is running the page redirect part, toolbarBean is being reset and activeIndex starts off at 0 again. Does anyone have any ideas on what I'm doing wrong?
Have a look at my answer in this question:
How to change activeindex in TabMenu
I think it's what you are looking for.
Edit:
Here is the complete answer:
I have found a solution in the PrimeFaces showcase. You can add a request parameter to the menuitem and append this parameter to your url. So no backing bean is needed to keep the active index:
<p:tabMenu activeIndex="#{param.i}">
<p:menuitem value="Home" icon="ui-icon-home" url="page1.xhtml?i=0">
<f:param name="i" value="0" />
</p:menuitem>
<p:menuitem value="Search" icon="ui-icon-search" url="page2.xhtml?i=1">
<f:param name="i" value="1" />
</p:menuitem>
...
</p:tabMenu>
I've unsuccessfully tried to reproduce the error.
I believe that the code sample I used has the features you need. Check it out:
*The View
<p:layout fullPage="true">
<p:layoutUnit position="north" size="100" header="Top" resizable="true" closable="true" collapsible="true">
<h:form id="formMenu">
<p:tabMenu id="tabMenu" activeIndex="#{toolbarBean.currentTab}">
<p:menuitem value="Main" action="#{toolbarBean.changeActiveIndex(0)}" />
<p:menuitem value="Page2" action="#{toolbarBean.changeActiveIndex(1)}" />
<p:menuitem value="Page3" action="#{toolbarBean.changeActiveIndex(2)}" />
<p:menuitem value="Page4" action="#{toolbarBean.changeActiveIndex(3)}" />
</p:tabMenu>
</h:form>
</p:layoutUnit>
<p:layoutUnit position="south" size="100" header="Bottom" resizable="true" closable="true" collapsible="true">
<h:outputText value="South unit content." />
</p:layoutUnit>
<p:layoutUnit position="west" size="200" header="Left" resizable="true" closable="true" collapsible="true">
<h:outputText value="West unit content." />
</p:layoutUnit>
<p:layoutUnit position="east" size="200" header="Right" resizable="true" closable="true" collapsible="true" effect="drop">
<h:outputText value="Right unit content." />
</p:layoutUnit>
<p:layoutUnit position="center">
MAIN
</p:layoutUnit>
</p:layout>
The Managed Bean
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class ToolbarBean implements Serializable {
private int currentTab;
public int getCurrentTab() {
return currentTab;
}
public void setCurrentTab(int currentTab) {
this.currentTab = currentTab;
}
public String changeActiveIndex(int currentTab) {
this.currentTab = currentTab;
switch (currentTab) {
case 0:
return "main";
case 1:
return "page2";
case 2:
return "page3";
case 3:
return "page4";
default:
return "page5";
}
}
}
Note that the ToolbarBean is almost the same. As you are using #Named, see if the ManagedBean is indeed in session scope.
Another thing worth mentioning is that main.xhml, page1.xhtml, page2.xhtml and etc, has the same content except for the center layout unit content.
I use a facelets login form for Spring Security:
<h:messages globalOnly="true" layout="table" />
<h:form id="formLogin" prependId="false">
<h:outputLabel for="j_username" value="Usuario:" />
<h:inputText id="j_username" value="#{autenticacionController.administrador.login}" />
<h:outputLabel for="j_password" value="ContraseƱa:" />
<h:inputSecret id="j_password" value="#{autenticacionController.administrador.password}" />
<h:commandButton value="Entrar" action="#{autenticacionController.loginAction}" />
<h:commandButton value="Cancelar" immediate="true" action="#{autenticacionController.cancelarAction}" />
</h:form>`
The loginAction method forwards the request with this:
FacesContext.getCurrentInstance().getExternalContext().dispatch("/j_spring_security_check")
It works fine, but how can I show a facesmessage in my h:messages tag if the BadCredentials exception is thrown by Spring Security?
I know it can be done with a phase listener, but I don't like that way (dealing with exceptions in listeners).
I'm trying another way, configuring Spring Security like this:
authentication-failure-url="/faces/paginas/autenticacion/login.xhtml?error=1
And then in the login page, catch the GET param "error". But how can I show the facesmessage in this way?
Another way I tried was to override the messages properties file of Spring Security (overriding the message for the key "badcredentials"), but it didn't work neither (I didn't know how to show the message).
Anyone know how to do it?
Thank you very much in advance.
And then in the login page, catch the GET param "error". But how can I show the facesmessage in this way?
This way:
<f:metadata>
<f:viewParam name="error" validator="#{auth.checkErrors}" />
</f:metadata>
<h:messages />
with
public void checkErrors(FacesContext context, UIComponent component, Object value) {
if ("1".equals(value)) {
throw new ValidatorException(new FacesMessage("Invalid credentials"));
}
}
or maybe so:
<f:metadata>
<f:viewParam name="error" value="#{auth.error}" />
<f:event type="preRenderView" listener="#{auth.checkErrors}" />
</f:metadata>
<h:messages />
with
private int error;
public void checkErrors() {
if (error == 1) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Invalid credentials"));
}
}
Either way, this feels pretty hacky :)