2014-06-24 2 views
3

Я написал (ну, модифицированный) настраиваемый компонент для переключателей, появляющихся внутри datatable на основе this article. Я немного изменил его для JSF 2.2, используя теги @FacesRenderer и @FacesComponent и опустив класс пользовательских тегов. Все отлично работает в моей XHTML страницеСоздание пользовательского компонента JSF 2.2 динамически

xmlns:custom="http://mynamespace" 
... 
<h:dataTable id="mySampleTable3" 
      value="#{myBackingBean.sampleTable3}" 
      var="item" 
      width="100%" 
      border="1" 
      first="0">   
    <f:facet name="header"> 
     <h:panelGrid id="gridHeader3" columns="2" width="100%" > 
       <h:outputText id="outputText3" 
         value="Radios grouped in row(With our custom tag)"/> 
     </h:panelGrid> 
    </f:facet> 
    <h:column> 
     <f:facet name="header"> 
       <h:outputText value="Emp Name" /> 
     </f:facet> 
     <h:outputText id="empName" value="#{item.empName}"/> 
    </h:column>   
    <h:column> 
     <f:facet name="header"> 
       <h:outputText value="Excellent" /> 
     </f:facet> 
     <custom:customradio id="myRadioId2" 
          name="myRadioRow" 
          value="#{item.radAns}" 
          itemValue="E" />      
    </h:column>   
    <h:column> 
     <f:facet name="header"> 
        <h:outputText value="Good" /> 
     </f:facet> 
     <custom:customradio id="myRadioId3" 
          name="myRadioRow" 
          value="#{item.radAns}" 
          itemValue="G" />      
    </h:column> 
    ...  
</h:dataTable> 

и DataTable радиокнопок работают правильно (они сгруппированы по строкам). Проблема возникает, когда я использую этот компонент динамически. Таким образом, у меня есть

HtmlPanelGrid radioGrid = (HtmlPanelGrid) getApplication() 
          .createComponent(HtmlPanelGrid.COMPONENT_TYPE); 

... 

UICustomSelectOneRadio customSelectOneRadio = (UICustomSelectOneRadio) getApplication(). 
        createComponent(UICustomSelectOneRadio.COMPONENT_TYPE); 


customSelectOneRadio.setId("rb_" + question.getQuestionId() + "_" + row + "_" + col); 
customSelectOneRadio.setName("myRadioRow"); 

String binding = "#{" + beanName + "." + propName + 
       "[\"" + question.getQuestionId().toString() + "_" + 
       subQuestion.getId() + "\"]}"; 

ValueExpression ve = getExpressionFactory(). 
        createValueExpression(getELContext(), binding, String.class); 
customSelectOneRadio.setValueExpression("value", ve); 

customSelectOneRadio.setItemValue(value); 

radioGrid.getChildren().add(customSelectOneRadio); 

К сожалению, несмотря на то, что данные передаются, переключателями нет. Появляются пустые столбцы. Мне было интересно, знает ли кто-нибудь, есть ли какой-то метод, который я должен переопределять в моем рендерере. Шахта выглядит следующим образом:

@FacesRenderer(componentFamily = UICustomSelectOneRadio.COMPONENT_FAMILY, rendererType = HTMLCustomSelectOneRadioRenderer.RENDERER_TYPE) 
public class HTMLCustomSelectOneRadioRenderer extends Renderer { 

    public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer"; 

    @Override 
    public void decode(FacesContext context, UIComponent component) { 
     if ((context == null) || (component == null)) { 
      throw new NullPointerException(); 
     } 

     UICustomSelectOneRadio customSelectOneRadio; 
     if (component instanceof UICustomSelectOneRadio) { 
      customSelectOneRadio = (UICustomSelectOneRadio) component; 
     } else { 
      return; 
     } 

     Map map = context.getExternalContext().getRequestParameterMap(); 
     String name = getName(customSelectOneRadio, context); 
     if (map.containsKey(name)) { 
      String value = (String) map.get(name); 
      if (value != null) { 
       setSubmittedValue(component, value); 
      } 

     } 
    } 

    private void setSubmittedValue(UIComponent component, Object obj) { 
     if (component instanceof UIInput) { 
      ((UIInput) component).setSubmittedValue(obj); 
     } 
    } 


    private String getName(UICustomSelectOneRadio customSelectOneRadio, 
      FacesContext context) { 

     UIComponent parentUIComponent 
      = getParentDataTableFromHierarchy(customSelectOneRadio); 
     if (parentUIComponent == null) { 
      return customSelectOneRadio.getClientId(context); 
     } else { 
      if (customSelectOneRadio.getOverrideName() != null 
        && customSelectOneRadio.getOverrideName().equals("true")) { 
       return customSelectOneRadio.getName(); 
      } else { 
       String id = customSelectOneRadio.getClientId(context); 
       int lastIndexOfColon = id.lastIndexOf(":"); 
       String partName = ""; 
       if (lastIndexOfColon != -1) { 
        partName = id.substring(0, lastIndexOfColon + 1); 
        if (customSelectOneRadio.getName() == null) { 
         partName = partName + "generatedRad"; 
        } else { 
         partName = partName + customSelectOneRadio.getName(); 
        } 
       } 
       return partName; 
      } 
     } 
    } 

    private UIComponent getParentDataTableFromHierarchy(UIComponent uiComponent) { 
     if (uiComponent == null) { 
      return null; 
     } 
     if (uiComponent instanceof UIData) { 
      return uiComponent; 
     } else { 
      //try to find recursively in the Component tree hierarchy 
      return getParentDataTableFromHierarchy(uiComponent.getParent()); 
     } 
    } 

    @Override 
    public void encodeEnd(FacesContext context, UIComponent component) 
      throws IOException { 
     if ((context == null) || (component == null)) { 
      throw new NullPointerException(); 
     } 

     UICustomSelectOneRadio customSelectOneRadio 
      = (UICustomSelectOneRadio) component; 

     if (component.isRendered()) { 
      ResponseWriter writer = context.getResponseWriter(); 

      writer.startElement("input", component); 
      writer.writeAttribute("type", "radio", "type"); 
      writer.writeAttribute("id", component.getClientId(context), "id"); 
      writer.writeAttribute("name", getName(customSelectOneRadio, context), "name"); 

      if (customSelectOneRadio.getStyleClass() != null && customSelectOneRadio.getStyleClass().trim(). 
        length() > 0) { 
       writer.writeAttribute("class", customSelectOneRadio.getStyleClass().trim(), "class"); 
      } 
      if (customSelectOneRadio.getStyle() != null && customSelectOneRadio.getStyle().trim().length() > 0) { 
       writer.writeAttribute("style", customSelectOneRadio.getStyle().trim(), "style"); 
      } 

     ... 

      if (customSelectOneRadio.getValue() != null 
        && customSelectOneRadio.getValue().equals(customSelectOneRadio.getItemValue())) { 
       writer.writeAttribute("checked", "checked", "checked"); 
      } 

      if (customSelectOneRadio.getItemLabel() != null) { 
       writer.write(customSelectOneRadio.getItemLabel()); 
      } 
      writer.endElement("input"); 
     } 
    } 

} 

Чтобы быстро подвести итог, это работает, когда я использую пользовательский тег в файле Facelets/XHTML, но не тогда, когда я использую его динамически, например,

<h:panelGroup id="dynamicPanel" layout="block" binding="#{documentController.dynamicPanel}"/> 

Если у кого-то есть идеи о том, что я могу отсутствовать, я был бы признателен.

ответ

0

Gotcha! Все это вращалось вокруг изменения свойства rendererType. Из класса Renderer выше, вы можете видеть, у меня было установлено в мой собственный, т.е.

public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer";

(1) я изменил его на

public static final String RENDERER_TYPE = "javax.faces.Radio";

(2) Я также изменил свой файл xxx.taglib.xml, чтобы отразить это, например

<namespace>http://mycomponents</namespace> 
<tag> 
    <tag-name>customradio</tag-name> 
    <component> 
     <component-type>CustomRadio</component-type> 
     <renderer-type>javax.faces.Radio</renderer-type>  /*<----- See Here */ 
    </component> 
</tag> 

(3) Но вот последний шаг к сведению. Мне также пришлось создать следующий конструктор в моем UICustomSelectOneRadio компоненте. Без этого тоже не получилось.

public UICustomSelectOneRadio() { 
    this.setRendererType("javax.faces.Radio"); 
} 

Помните - я протянул компонент из UIInput, так javax.faces.Text был бы установлен. Оставляя мой component family как «CustomRadio», все в порядке.

Я все еще не 100% комфортно с типами визуализатора и т.д., но и для тех, кто хочет немного информации, BalusC объясняет это хорошо here.

Смежные вопросы