Search code examples
jsfcustom-componentuirepeat

Custom component inside <ui:repeat> doesn't find iterated item during encode


I'm trying to create a custom component for displaying an Entity with a certain form. So I've created my @FacesComponent and he's working but only when he is not inside a loop like <ui:repeat>. When I'm using the following code, my component is displaying null values for price and photo but not for name. Do you have an explaination ?

XHTML code :

<ui:define name="content">
    <f:view>
        <h:form>
            <ui:repeat value="#{dataManagedBean.listNewestCocktails}" var="item" varStatus="status">
                <h:outputText value="#{item.price}"/> <!--working very well-->
                <t:cocktailVignette idPrefix="newCocktails" name="foo" price="#{item.price}" urlPhoto="#{item.photoURI}"/>   <!-- not working the getPrice here -->
            </ui:repeat>

            <!--<t:cocktailVignette idPrefix="allCocktails" name="OSEF" price="20" urlPhoto="osefdelurl" ></t:cocktailVignette> -->
        </h:form>               
    </f:view> 

My component code :

    package component;

import java.io.IOException;
import javax.faces.context.FacesContext;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.ResponseWriter;

@FacesComponent(value = "CocktailVignette")
public class CocktailVignette extends UIComponentBase {

    private String idPrefix;
    private String name;
    private String price;
    private String urlPhoto;

    public String getIdPrefix() {
        return idPrefix;
    }

    public void setIdPrefix(String idPrefix) {
        this.idPrefix = idPrefix;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getUrlPhoto() {
        return urlPhoto;
    }

    public void setUrlPhoto(String urlPhoto) {
        this.urlPhoto = urlPhoto;
    }

    @Override
    public String getFamily() {
        return "CocktailVignette";
    }

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.write("<div id=\""+idPrefix+name+"\" class=\"cocktail-vignette\">");
        writer.write("<h2>"+name+"</h2>");
        writer.write("<h3>"+price+"</h3>");
        writer.write("</div>");
    }
}

Thanks a lot :) I'm trying but nothing is working ...


Solution

  • All of component's attributes which are sensitive to changes in state (e.g. the value being dependent on <ui:repeat var>, at least those which is not known during view build time but during view render time only), must delegate the storage of attribute value to the state helper as available by inherited getStateHelper() method.

    Kickoff example:

    public String getPrice() {
        return (String) getStateHelper().eval("price");
    }
    
    public void setPrice(String price) {
        getStateHelper().put("price", price);
    }
    

    Apply the same for all other attributes and get rid of the instance variable declarations. Important note is that the state helper key ("price" in above example) must be exactly the same as attribute name.

    See also: