当前位置: 动力学知识库 > 问答 > 编程问答 >

jsf - How can I do this with a custom/composite component?

问题描述:

I tried to ask a more specific question but I think i may have been overly specific so I'll zoom out to get a better advice.

I'm trying to create a composite/custom component that will accept two attributes

  1. A list of strings --> names of fields
  2. a list of lists of <String, Value> --> groups of fields with <fieldName, fieldValue>

example values:

  1. [Street, Country, State, ZipCode]
  2. [{(Street, Greenfield), (Country, USA)}, {(Country, Canada), (ZipCode, 3333)}]

The component will render this, based on the attributes:

The reason I am having difficulties with this component is that I don't know what's the proper way to maintain placeholders for fields that were not entered originally but can be added by the user through the component.

In the above example, for the first set, these would be State and ZipCode.

My Idea was to create a dummy object with all the fields and on submittion copy the dummy object's values to the data structure passed in the attributes.

The problem I was facing was not knowing how to read the values on component creation and altering the List passed through the attribute on submission.

I will add sample code soon (Although that shouldn't be a crucial for answering this question)

Thank you just for reading this far!! :-)

My Code (Again, not required for answering the question but may be helpful to understand my challenge)

The Composite Component Code:

<cc:interface componentType="dynamicFieldList">

<cc:attribute name="styleClass" default="fieldType" />

<cc:attribute name="form" default="@form"

shortDescription="If used, this is the name of the form that will get executed" />

<cc:attribute name="list" type="java.util.List" required="true"

shortDescription="The values of the list. The type must be List of FieldGroup" />

<cc:attribute name="groupTypes" type="java.util.List" required="true"

shortDescription="The types that will be available to choose from for each field. The type of this must be List of FieldType" />

</cc:interface>

<cc:implementation>

<h:dataTable id="table" value="#{cc.model}" var="fieldGroup">

<h:column>

<ui:repeat var="field" value="#{fieldGroup.values}">

<utils:fieldType value="#{field.value}" type="#{field.type}"/>

</ui:repeat>

</h:column>

<h:column>

<h:commandButton value="delete" action="#{cc.remove}">

<f:ajax render="#{cc.attrs.form}" execute="#{cc.attrs.form}" />

</h:commandButton>

</h:column>

</h:dataTable>

<h:commandButton value="add" action="#{cc.add}">

<f:ajax render="#{cc.attrs.form}" execute="#{cc.attrs.form}" />

</h:commandButton>

</cc:implementation>

The component bean java code:

@FacesComponent(value = "dynamicFieldGroupList")

// To be specified in componentType attribute.

@SuppressWarnings({ "rawtypes", "unchecked" })

// We don't care about the actual model item type anyway.

public class DynamicFieldGroupList extends UIComponentBase implements NamingContainer

{

private transient DataModel model;

private List<FieldGroup> displayList;

public DynamicFieldGroupList()

{

super();

List<FieldGroup> list = new ArrayList<FieldGroup>();

for (FieldGroup group : getList()){

FieldGroup currGroup = new FieldGroup("Untitled");

//Assumption - Each type may exist only once in a group.

Map<FieldType, FieldDetail> hash = new HashMap<FieldType, FieldDetail>();

for (FieldDetail detail: group.getDetails()){

hash.put(detail.getType(), detail);

}

// While creating the dummy object, insert values the exist or placeholders if they don't.

for (FieldType type : getGroupTypes()){

if (hash.containsKey(type)){

currGroup.addDetail(hash.get(type));

} else {

currGroup.addDetail(new FieldDetail(type,null));

}

}

list.add(currGroup);

}

// Assign the created list to be the displayed (dummy) object

setDisplayList(list);

}

public void add()

{

// Add a new group of placeholders

FieldGroup group = new FieldGroup("Untitled");

for (FieldType type: getGroupTypes()){

group.addDetail(new FieldDetail(type, null));

}

getList().add(group);

}

public void remove()

{

getDisplayList().remove(model.getRowData());

}

@Override

public String getFamily()

{

return "javax.faces.NamingContainer"; // Important! Required for

// composite components.

}

public DataModel getModel()

{

if (model == null)

model = new ListDataModel(getDisplayList());

return model;

}

private List<FieldGroup> getList()

{ // Don't make this method public! Ends otherwise in an infinite loop

// calling itself everytime.

return (List) getAttributes().get("list");

}

private List<FieldType> getGroupTypes()

{ // Don't make this method public! Ends otherwise in an infinite loop

// calling itself everytime.

return (List) getAttributes().get("groupTypes");

}

public void setDisplayList(List<FieldGroup> displayList)

{

this.displayList = displayList;

}

public List<FieldGroup> getDisplayList()

{

return displayList;

}

}

网友答案:

You can use a Map to hold the values for each group.

Map<String, Object> values = new HashMap<String, Object>();

Assuming that you've all those maps in a List<Map<String, Object>> and the field names in a List<String>, then you can get/set them all basically as follows

<ui:repeat value="#{allValues}" var="values">
    <ui:repeat value="#{fieldNames}" var="fieldName">
        <h:outputLabel value="#{fieldName}" />
        <h:inputText value="#{values[fieldName]}" />
        <br/>
    </ui:repeat>
</ui:repeat>
分享给朋友:
您可能感兴趣的文章:
随机阅读: