jsf 2 - JSF Composite Component for MultilingualString object -


i'm writing jsf application needs internationalized. so, created multilingualstring :

public class multilingualstring {     /* language class wrapper java.util.locale */     private map<language, string> strings;      /* business methods, getters, setters */ } 

now, there multiple forms needs fill multilingualstring, , it's quite ugly repeat c:foreach loop each time need put such object in form. heard jsf composite components, , tried write 1 purpose.

here inputmultilingualstring.xhtml :

<ui:component xmlns:h="http://xmlns.jcp.org/jsf/html"               xmlns:composite="http://xmlns.jcp.org/jsf/composite"               xmlns:f="http://xmlns.jcp.org/jsf/core"               xmlns:ui="http://xmlns.jcp.org/jsf/facelets">     <composite:interface componenttype="inputmultilingualstring">         <composite:attribute name="value" required="true"                              type="com.tob.entities.internationalization.multilingualstring"/>         <composite:attribute name="languages" type="java.util.list" default="#{null}"/>     </composite:interface>      <composite:implementation>         <f:event type="prerendercomponent" listener="#{cc.init}"/>         <h:datatable id="#{cc.clientid}" value="#{cc.languages}" var="language">             <h:column>                 <h:outputlabel value="#{language}"/>             </h:column>             <h:column>                 <h:inputtext binding="#{cc.inputs[language]}"/>             </h:column>         </h:datatable>     </composite:implementation> </ui:component> 

so want value attribute instance of multilingualstring , languages attribute instance of list of language. if languages attribute null, want te composite component display row in datatable each entry in map contained in multilingualstring.

now here "backing component" in inputmultilingualstring.java :

@facescomponent(value = "inputmultilingualstring", createtag = true) public class inputmultilingualstring extends uiinput implements namingcontainer {      private final map<language, uiinput> inputs = new hashmap();     private list<language> languages;      @override     public string getfamily() {         return (uinamingcontainer.component_family);     }      public void init() {         list<language> ls = (list<language>) this.getattributes().get("languages");         multilingualstring ms = (multilingualstring) this.getvalue();          /* setting languages */         if (ls != null) {             this.setlanguages(ls);         } else {             this.languages = new arraylist();             this.languages.addall(ms.getstrings().keyset());         }          /* initializing inputs */         uiinput tmp;         (language l : this.languages) {             tmp = new uiinput();             tmp.setvalue(ms.getstring(l));//             this.inputs.put(l, tmp);         }     }      @override     public string getsubmittedvalue() {         string ret = new string();          (map.entry<language, uiinput> entry : this.inputs.entryset()) {             if (entry.getvalue() != null) {                 if (!ret.isempty()) {                     ret += ',';                 }                 ret += entry.getkey().getlanguagetag(); // nullpointerexception here when form submitted                 ret += "=" + entry.getvalue().getsubmittedvalue();             }         }         return (ret);     }      @override     protected object getconvertedvalue(facescontext context, object submittedvalue) {         multilingualstring ms = (multilingualstring) this.getvalue();         string[] entries = ((string) submittedvalue).split(",");         string[] pair;         language language;          (string entry : entries) {             pair = entry.split("=");             language = new language();             language.setlanguagetag(pair[0]);             ms.addstring(language, pair[1]);         }         return (ms);     }      public list<language> getlanguages() {         return (this.languages);     }      public void setlanguages(list<language> languages) {         this.languages = languages;     }      public map<language, uiinput> getinputs() {         return (this.inputs);     } } 

in order implement rule on languages want display input, added languages attribute backing component , initialized in init method called on prerendercomponent event. languages list correctly initialized.

here how use composite component :

<ui:composition template="/templates/common.xhtml"                 xmlns="http://www.w3.org/1999/xhtml"                 xmlns:h="http://xmlns.jcp.org/jsf/html"                 xmlns:ui="http://xmlns.jcp.org/jsf/facelets"                 xmlns:tob="http://xmlns.jcp.org/jsf/composite/components"                 xmlns:p="http://primefaces.org/ui">     <ui:define name="content">         <h:form id="testform">             <tob:inputmultilingualstring value="#{testbean.ms}" languages="#{testbean.languages}"/>             <!-- testbean.ms contains :                      [english]  => string-english                      [français] => string-français                      [русский]  => string-русский                   , testbean.languages contains list of language objects english, french, , russian -->             <p:commandbutton value="submit" action="#{testbean.submit()}"/>         </h:form>     </ui:define> </ui:composition> 

the problems :

  • if multilingualstring entered value contains strings, not displayed in inputtext if fill inputtext value attribute (i read article of balusc on topic , doesn't need fill value attribute make drop down lists have correct value). read on balusc's answer somewhere on stackoverflow uiinput referenced in binding attribute of inputtext created if it's evaluated null, that's why tried initialized them in init method, far no luck.
  • when submit form, nullpointerexception @ getkey() call in getsubmittedvalue() method. how possible ?

i hope clear , can me ! !

edit : i'm using glassfish 4 , manually updated mojarra 2.2.6

there 2 technical problems in code posted far:

  1. you're using binding on variable available during view render time. binding attribute runs during view build time, not during view render time. in particular case, when binding executed, #{language} null. see how 'binding' attribute work in jsf? when , how should used?. seem expect multiple <h:inputtext> components generated, that's not true. there's 1 reused multiple times during rendering view. when have used <c:foreach> instead of <h:datatable>, indeed physically multiple <h:inputtext> components generated. see jstl in jsf2 facelets... makes sense?

  2. you're not saving state of component postback. should removing languages property , let getter , setter delegate getstatehelper(). see how save state when extending uicomponentbase.

however, overall approach clumsy. don't need backing component functional requirement. add list<languages> getter multilingualstring , use directly default of languages attribute.

so, if add multilingualstring:

public list<language> getlanguages() {     return new arraylist<>(strings.keyset()); } 

and reference attribtues via #{cc.attrs}:

<cc:interface>     <cc:attribute name="value" required="true" type="com.tob.entities.internationalization.multilingualstring"/>     <cc:attribute name="languages" type="java.util.list" default="#{cc.attrs.value.languages}" /> </cc:interface>  <cc:implementation>     <h:datatable value="#{cc.attrs.languages}" var="language">         <h:column>             <h:outputlabel value="#{language}"/>         </h:column>         <h:column>             <h:inputtext value="#{cc.attrs.value.strings[language]}" />         </h:column>     </h:datatable> </cc:implementation> 

then should work intented. note possibility use brace notation [] reference dynamic map key. that's perhaps whole key solution (you seems not aware of , hence working towards overcomplicated solution).


Comments

Popular posts from this blog

commonjs - How to write a typescript definition file for a node module that exports a function? -

openid - Okta: Failed to get authorization code through API call -

thorough guide for profiling racket code -