De multe ori aplicatiile trebuie sa raspunda evenimentelor utilizator, precum selectarea item- ilor unui meniu sau click pe un buton. Facem urmatorul scenariu: dorim sa selectam o tara dintr-un formular prin schimbarea localizarii si sa reincarcam pagina. In mod obisnuit inregistram manipulatori de evenimente componentelor. In cazul exemplului dat vom inregistra un value change listener unui meniu din pagina JF: <h:selectOneMenu valueChangeListener="#{form.countryChanged}"... ... <!h:selectOneMenu !aloarea atributului refera catre metoda countryChanged"# din bean-ul numit form. "ceasta metoda este invocata de implementarea JF dupa ce utilizatorul face o selectie. JF suporta trei categorii de evenimente: - value change, sunt declansate de componente de intrare precum: <h:in$ut%e&t, <h:selectOne'adio, si <h:selectManyMenu, atunci cand valoarea componentei se schimba - action, sunt declansate de componentele de comanda precum: <h:command(utton si <h:commandLin), atunci cand butonul sau link-ul sunt activate - phase, sunt declansate de ciclul de viata al JF. #vent listenerele pot afecta ciclul de viata al JF-ului in unul dintre urmatoarele moduri: o pastreaza ciclul normal o apeleaza *acesConte&t.render'es$onse"# pentru a sari partea ramasa din ciclu pina la faza $ender $esponse o apeleaza *acesConte&t.res$onseCom$lete"# pentru a sari intreg ciclul de viata ramas In aplicatia pe care am construit-o am creat un formular cu mai multe campuri text, o lista de selectie a tarilor si un buton de trimitere. Dupa selectarea tarii si apasarea butonului toate etichetele se vor traduce dupa tara selectata. Fisierul %sp va contine pentru butonul de trimitere urmatorul cod: <h:selectOneMenu value="#{form.country}" onchange="submit()" valueChangeListener="#{form.countryChanged}" <f:select+tems value="#{form.country,ames}" ! <!h:selectOneMenu Functia Javacript su-mit"# este invocata pentru transmiterea datelor din meniu formularului si invocarea ciclului de viata al JF-ului. Dupa faza de &rocess !alidation, JF invoca metoda countryChanged"#. "ceasta modifica locale-ul vie' root-ului, functie de valoarea noii tari. public void countryChanged".alueChange/vent event# { *acesConte&t conte&t = *acesConte&t.getCurrentInstance"#0 if "US.e1uals""2tring# event.get,e3.alue"### conte&t.get.ie3'oot"#.setLocale"Locale.US#0 else conte&t.get.ie3'oot"#.setLocale"new Locale""ro"4"'o"##0 } (a oricare listener si acesta trimite ca parametru un eveniment corespunzator. )istenerul utilizeaza acest eveniment pentru a accesa noua valoare a componentei. (lasa .alueChange/vent extinde *aces/vent, ambele din 5ava&.faces.event. In bean mai exista macar urmatoarele atribute si metode: private 2tring country0 private static final 2tring US = "6nited 2tates"0 private static final 2tring ROMANIA = "'omania"0 private static final 2tring78 COUNTRY_NAMES = { ROMANIA4 US }0 private static 9rrayList<2elect+tem countryItems = null0 !! :'O:/'%;: country,ames public Collection<2elect+tem getCountry,ames"# { if "countryItems == null# { countryItems = new 9rrayList<2elect+tem"#0 for "int i = <0 i < COUNTRY_NAMES.length0 i==# { countryItems.add"new 2elect+tem"COUNTRY_NAMES7i8##0 } } return countryItems0 } Evenimente Action (omponentele de comanda trimit cereri atunci cand ele sunt activate, de aceea nu este necesar sa utilizam onchange pentru a forta trimiterea asa cum faceam pentru value change. (and activam o comanda sau un link forma ce o contine este trimisa si implementarea JF declanseaza si evenimentul. #ste important sa facem distinctia intre action listener-I si actiuni. "ctiunile sunt destinate business logic-ului si participa la navigare, in timp ce action listener-I efectueaza logica interfetei neparticipand la navigare. *neori cele doua lucreaza impreuna atunci cand o actiune necesita informatii despre interfata utilizator. pre exemplu: <h:command(utton image="image.5$g" actionListener="#{form.listen}" action="#{form.act}"! cand facem click pe imagine +de fapt o harta de imagine,, un listener va determina, pe baza coordonatelor unde am facut click, care dintre pagini se va incarca. action va transmite efectiv prin apelul functiei act"# valoarea 2tring-ului cu noua pagina. Determinarea coordonatelor unde a facut mouse-ul click se poate determina astfel: *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 2tring client+d = e.getCom$onent"#.getClient+d"conte&t#0 Ma$ re1uest:arams = conte&t.get/&ternalConte&t"#.get'e1uest:arameterMa$"#0 int &= ne3 +nteger""2tring# re1uest:arams.get"client+d = ".&"##.int.alue"#0 int y= ne3 +nteger""2tring# re1uest:arams.get"client+d = ".y"##.int.alue"#0 &utem folosi, de asemenea, pentru a adauga listener-i unei componente si urmatoarele tag-uri: <f:actionListener si <f:valueChangeListener. Iata un exemplu de echivalenta: <h:selectOneMenu value="#{form.country}" onchange="su-mit"#" valueChangeListener="#{form.countryChanged}" <f:select+tems value="#{form.country,ames}"! <!h:selectOneMenu $espectiv: <h:selectOneMenu value="#{form.country}" onchange="su-mit"#" <f:valueChangeListener ty$e="com.core5sf.CountryListener"! <f:select+tems value="#{form.country,ames}"! <!h:selectOneMenu -ag-urile au urmatorul avanta% fata de atribute: putem adauga listener-i multipli unei singure componente. In exemplul anterior atributul specifica o metoda in timp ce tag-ul o clasa. (lasa va trebui sa implementeze .alueChangeListener si in consecinta metoda $u-lic void $rocess.alueChange".alueChange/vent event# "nalog pentru action. .bservatie: daca specificam mai multi listener-i unei componente, acestia sunt invocati in urmatoarea ordine: /. cei specificati de atribut 0. cei specificati de tag-uri, in ordinea in care au fost declarati Detalii despre atributul immediate #venimentele immediate sunt lansate dupa faza "ppl1 $e2uest !alues. &entru componentele input conversia si validarea sunt facute dupa faza "ppl1 $e2uest !alues si evenimentul value change este declansat ulterior. &entru componentele de comanda action listener-i sunt invocati urmati de actions. "cest proces este gestionat de navigator si ocoleste restul ciclului de viata pina la $ender $esponse. In cazul exemplului discutat relativ la internationalizare. &resupunem ca avem un camp text pentru adresa pe care il dorim re2uired, cu mesa%ul corespunzator. 3esa%ul va aparea in momentul in care am selectat tara. "m dori ca mesa%ul sa apara in momentul in care apasam butonul de trimitere a formularului. olutia ar fi action listener-ul din lista de selectie sa se desfasoare imediat. Dupa cum am spus componentele input immediate efectueaza conversii si validari si pe urma livreaza valorile modificate la inceputul ciclului de viata +dupa "ppl1 $e2uest !alues,, in loc de &rocess !alidations. Deocamdata, prin aceasta, am ocolit faza de procesare a validarilor, dar acestea tot se intampla si erorile tot vor fi afisate. &entru a preveni validarile altor componente din forma va trebui sa apelam metoda render'es$onse"# la sfarsitul implementarii listener-lui. "pelul metodei va sari peste toate celelalte faze. "sadar, pentru a sari validarea atunci cand se declanseaza un eveniment value change avem de facut urmatoarele: >. sa adaugam immediate="true" tag-ului input ?. sa apelam conte&t.render'es$onse"#0 la sfarsitul implementarii listener-ului .bservatie: in cazul unei componente de comanda nu este necesar pasul doi. "colo actiunile determina trecerea automata in starea $ender $esponse. Trimiterea valorilor de la UI la server In scenariul internationalizarii, presupunem ca in locul listei de selectie avem doua steaguri, cu cele doua tari. Facand click pe oricare dintre ele limba se schimba corespunzator. In fisierul %sp am avea urmatorul cod: <h:commandLin) action="#{form.u299ction}" immediate="true" <h:gra$hic+mage value="!usa@flag.gif" style="-order: <$&"! <!h:commandLin) i asemanator pentru $omania. <h:commandLin) action="#{form.rO9ction}" immediate="true" <h:gra$hic+mage value="!ro@flag.gif" style="-order: <$&"! <!h:commandLin) Fiecare link apeleaza o alta metoda, desi implementarile lor nu sunt mult diferite: $u-lic 2tring u299ction"# { *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 conte&t.get.ie3'oot"#.setLocale"Locale.US#0 return null0 } $u-lic 2tring rO9ction"# { *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 conte&t.get.ie3'oot"#.setLocale"new Locale""ro"4"'o"##0 return null0 } Faptul ca metodele returneaza null indica faptul ca paginile reincarca aceleasi pagini. &entru a reduce codul redundant este preferabil sa transmitem codul de limba de la *I la server. !om scrie astfel un singur action sau action listener pentru a schimba localizarea vie' root-ului. "vem la dispozitie trei mecanisme de trimitere a informatiilor de la *I la server: - <f:$aram, ne permite sa atasam un parametru unei componente. -ag-ul are un comportament diferit, dependent de tipul de componenta caruia ii este atasat. pre exemplu, daca este atasat unui <h:out$ut%e&t parametrul este utilizat pentru completarea placeholder-elor precum {<}, {>}, etc. Daca este atasat unei componente de comanda va transmite valoarea parametrului, serverului ca un re2uest parameter. In cazul nostru codul ar fi: <h:commandLin) immediate="true" action="#{form.changeLocale}" <f:$aram name="languageCode" value="us"! <h:gra$hic+mage value="!usa@flag.gif" style="-order: <$&"! <!h:commandLin) ... <h:commandLin) immediate="true" action="#{form.changeLocale}" <f:$aram name="languageCode" value="ro"! <h:gra$hic+mage value="!ro@flag.gif" style="-order: <$&"! <!h:commandLin) &e server obtirea valorii am putea-o face astfel: $u-lic 2tring changeLocale"# { *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 2tring languageCode = getLanguageCode"conte&t#0 conte&t.get.ie3'oot"#.setLocale"ne3 Locale"languageCode##0 return null0 } $rivate 2tring getLanguageCode"*acesConte&t conte&t# { Ma$<2tring4 2tring $arams = conte&t.get/&ternalConte&t"#. get'e1uest:arameterMa$"#0 return $arams.get""languageCode"#0 } - <f:attri-ute, asemanator cu precedentul doar ca vom obtine valoarea dintre atributele componentei. (odul ar putea fi: <h:commandLin) immediate="true" actionListener="#{form.changeLocale}" <f:attri-ute name="languageCode" value="us"! <h:gra$hic+mage value="!us@flag.gif" style="-order: <$&"! <!h:commandLin) ... <h:commandLin) immediate="true" actionListener="#{form.changeLocale}" <f:attri-ute name="languageCode" value="ro"! <h:gra$hic+mage value="!ro@flag.gif" style="-order: <$&"! <!h:commandLin) De remarcat ca am modificat atributul din action in actionListener. 3odificarea se datoreaza faptului ca trebuie sa avem ca atribut al metodei changeLocale"# un 9ction/vent prin intermediul caruia accesam componenta ce a declansat evenimentul. &e server: $u-lic void changeLocale"9ction/vent event# { 6+Com$onent com$onent = event.getCom$onent"#0 2tring languageCode = getLanguageCode"com$onent#0 *acesConte&t.getCurrent+nstance"# .get.ie3'oot"#.setLocale"ne3 Locale"languageCode##0 } $rivate 2tring getLanguageCode"6+Com$onent com$onent# { Ma$<2tring4 O-5ect attrs = com$onent.get9ttri-utes"#0 return "2tring# attrs.get ""languageCode"#0 } - <f:set:ro$erty9ctionListener, reprezinta o scurtatura in evaluarea proprietatii. etarea acesteia se va face in backing bean. <h:commandLin) immediate="true" action="#{form.changeLocale}" <f:set:ro$erty9ctionListener target="#{form.languageCode}" value="us"! <h:gra$hic+mage value="!us@flag.gif" style="-order: <$&"! <!h:commandLin) ... <h:commandLin) immediate="true" action="#{form.changeLocale}" <f:set:ro$erty9ctionListener target="#{form.languageCode}" value="ro"! <h:gra$hic+mage value="!ro@flag.gif" style="-order: <$&"! <!h:commandLin) $espectiv, pe server: $rivate 2tring languageCode0 $u-lic 2tring changeLocale"# { *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 conte&t.get.ie3'oot"#.setLocale"ne3 Locale"languageCode##0 return null0 } $u-lic void setLanguageCode"2tring ne3.alue# { languageCode = ne3.alue0 } Phase events unt declansate inainte si dupa fiecare etapa a ciclului de viata. pecificarea listener-ilor pentru aceste evenimente se face in fisierul facesAconfig.&ml. &utem specifica oricati listener-i, ei fiind invocati in ordinea in care au fost specificati in fisierul de configurare. &utem crea phase listener-i prin implementarea interfetei :haseListener din pachetul 5ava&.faces.event. "ceasta are trei metode: :hase+d get:hase+d"#, transmite cand un anumit eveniment de faza sa fie livrat listener-ului. pre exemplu, daca returneaza :hase+d.9::L;@ '/B6/2%@.9L6/2 atunci metodele void after:hase":hase/vent# si void -efore:hase":hase/vent# vor fi apelate o sigura data in ciclul de viata: inaintea si dupa faza "ppl1 $e2uest .alues. +n caCul in care valoarea este :hase+d.9,;@:D92/ atunci metodele vor fi afisate de 4 ori. !om da o aplicatie in care utilizatorul isi poate selecta numele Id-ului de faza dintr-o lista cu o singura selectie. "ceasta va cuprinde intrarile: $rivate 2elect+tem78 $hases = { ne3 2elect+tem""'/2%O'/@.+/E"#4 ne3 2elect+tem""9::L;@'/B6/2%@.9L6/2"#4 ne3 2elect+tem"":'OC/22@.9L+F9%+O,2"#4 ne3 2elect+tem""6:F9%/@MOF/L@.9L6/2"#4 ne3 2elect+tem""+,.OG/@9::L+C9%+O,"#4 ne3 2elect+tem""'/,F/'@'/2:O,2/"#4 ne3 2elect+tem""9,;@:D92/"#4 }0 Implicit valoarea selectata fiind 9,;@:D92/. Dupa selectie +la un value change event, valoarea selectata este transmisa unui phase listener "im$lementat de core.5sf.:hase%rac)er,. public void $haseChange".alueChange/vent e# { Lifecycle*actory factory = "Lifecycle*actory# *actory*inder .getFactory"*actory*inder.IFECYCE_FACTORY#0 Lifecycle lifecycle = factory .getLifecycle"Lifecycle*actory.!EFAUT_IFECYCE#0 :haseListener78 listeners = lifecycle.get:haseListeners"#0 for "int i = <0 i < listeners.length0 i==# { :haseListener listener = listeners7i80 if "listener instanceof core.5sf.:hase%rac)er# ""core.5sf.:hase%rac)er# listener#.set:hase""2tring# e.get,e3.alue"##0 } } "vem asadar un singur $hase listener ce trimite mesa5e loggerAului: private static final Logger "ogger = Logger.getogger""core.5sf.$hases"#0 Implementarea este, pe scurt, urmatoarea: public void -efore:hase":hase/vent e# { "ogger.info""(/*O'/ " = e.get:hase+d"##0 } public void after:hase":hase/vent e# { "ogger.info""9*%/' " = e.get:hase+d"##0 } $u-lic :hase+d get:hase+d"# { if "$hase == null# { *acesConte&t conte&t = *acesConte&t.getCurrent+nstance"#0 $hase = "2tring#conte&t.get/&ternalConte&t"#.get+nit:arameter":D92/@:9'9M/%/'#0 } :hase+d $hase+d = :hase+d.9,;@:D92/0 if "$hase H= null# { if ""'/2%O'/@.+/E".e1uals"$hase## $hase+d = :hase+d.'/2%O'/@.+/E0 else if ""9::L;@'/B6/2%@.9L6/2".e1uals"$hase## $hase+d = :hase+d.9::L;@'/B6/2%@.9L6/20 else if "":'OC/22@.9L+F9%+O,2".e1uals"$hase## $hase+d = :hase+d.:'OC/22@.9L+F9%+O,20 else if ""6:F9%/@MOF/L@.9L6/2".e1uals"$hase## $hase+d = :hase+d.6:F9%/@MOF/L@.9L6/20 else if ""+,.OG/@9::L+C9%+O,".e1uals"$hase## $hase+d = :hase+d.+,.OG/@9::L+C9%+O,0 else if ""'/,F/'@'/2:O,2/".e1uals"$hase## $hase+d = :hase+d.'/,F/'@'/2:O,2/0 else if ""9,;@:D92/".e1uals"$hase## $hase+d = :hase+d.9,;@:D92/0 } return $hase+d0 } Id-ul fazei pentru listener este setat intr-un listbox, definit astfel: <h:selectOneList-o& valueChangeListener="#{form.$haseChange}" <f:select+tems value="#{form.$hases}" ! <!h:selectOneList-o& Iata cateva dintre rezultatele rularii: - fara sa selectam nimic, pentru ca avem setat default faza :hase+d.9,;@:D92/, listenerul va fi invocat inainte si dupa fiecare faza pina cand va fi selectat din lista si formularul trimis. In logger vom, insa, avea doar intrarile: +,*O: (/*O'/ '/2%O'/@.+/E > +,*O: 9*%/' '/2%O'/@.+/E > +,*O: (/*O'/ '/,F/'@'/2:O,2/ I +,*O: 9*%/' '/,F/'@'/2:O,2/ I "cest lucru se datoreaza faptului ca nu avem de restaurat niciun vie', pagina JF este incarcata pentru prima oara. 5eavand niciun arbore de componente nu are sens sa se faca conversii, validari, etc, deci se trece direct la faza de renderizare a raspunsului. Daca mai actionam o data butonul submit, fara alegerea unei faze, vom vedea toate fazele. - selectam acum "ppl1 $e2uest !alues si apasam butonul de trimitere. In logger vom gasi urmatoarele: +,*O: (/*O'/ '/2%O'/@.+/E > +,*O: 9*%/' '/2%O'/@.+/E > +,*O: (/*O'/ 9::L;@'/B6/2%@.9L6/2 ? +,*O: 9*%/' 9::L;@'/B6/2%@.9L6/2 ? +,*O: (/*O'/ :'OC/22@.9L+F9%+O,2 J $aspunsul se datoreaza faptului ca initial, atunci cand formularul a fost trimis, ID-ul avea inca valoarea 9,;@:D92/ si deci listenerul era interesat de toate fazele. "poi, ID- ul fazei este setat de un value change listener, dar acesta este invocat abia dupa faza &rocess !alidations. "bia la sfarsitul acestei faze se seteaza faza 9::L;@'/B6/2%@.9L6/2. (um aceasta faza a fost de%a executata listenerul nu va mai fi notificat pentru partea ramasa din ciclul de viata. - daca mai aplicam o data butonul de trimitere, fara alta selectie vom obtine: +,*O: (/*O'/ 9::L;@'/B6/2%@.9L6/2 ? +,*O: 9*%/' 9::L;@'/B6/2%@.9L6/2 ? 3otivul este ca nu mai trebuie sa schimbam selectia - selectam acum Invoke "pplication si trimitem forma. !om obtine la logger: +,*O: (/*O'/ 9::L;@'/B6/2%@.9L6/2 ? +,*O: 9*%/' 9::L;@'/B6/2%@.9L6/2 ? +,*O: (/*O'/ +,.OG/@9::L+C9%+O, K +,*O: 9*%/' +,.OG/@9::L+C9%+O, K #xplicatia: cand incepe ciclul de viata faza este "ppl1 $e2uest !alues asa ca listener- ul este notificat de aceasta. "poi, dupa &rocess !alidations, value change listener-ul modifica ID-ul de faza la +,.OG/@9::L+C9%+O,. In consecinta, listenerul este notificat de aceasta faza. Daca mai pasam o data butonul de trimitere, fara modificare in lista, obtinem doar: +,*O: (/*O'/ +,.OG/@9::L+C9%+O, K +,*O: 9*%/' +,.OG/@9::L+C9%+O, K