[JSF] Component tree leeg

Pagina: 1
Acties:

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Topicstarter
Ik ben bezig met een JSF AJAX component. Ik heb er al eerder enkele gemaakt, maar nu wil ik het extra netjes doen, namelijk door in mijn AJAX request gebruik te maken van dezelfde beans.

Het tonen van het component gaat goed, maar als ik een AJAX request doe, blijkt mijn component tree leeg te zijn. (context.getViewRoot() bevat geen children en findComponent() levert dus geen resultaat op)

In eerste instantie liet ik mijn request naar een speciale URL wijzen, maar dat leek mij de oorzaak van bovenstaand probleem. Dat dacht ik op te lossen door niet naar een AJAX URL te requesten, maar naar dezelfde URL als de pagina, maar dan met een extra GET parameter om aan te geven dat het een AJAX request is.

Dit levert echter hetzelfde resultaat op.

Ook op internet kan ik niet echt een goede oplossing vinden, waardoor ik nu bijna gedwongen ben om een variableresolver te gebruiken i.c.m. een propertyresolver die ik met de beans laat communiceren. Dit is echter een poepoplossing, want ik moet met de hand strings manipuleren om de variabele/propertynamen te verkrijgen.

Ik ben dus benieuwd waarom ik geen component tree krijg.

Ps 1: Ik gebruik een PhaseListener die ik ná de RESTORE_VIEW phase laat afgaan.
Ps 2: Ik gebruik MyFaces 1.1.3 en wil het straks onder Facelets (v1.1.11) laten werken.

Fat Pizza's pizza, they are big and they are cheezy


  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
JKVA schreef op zondag 20 augustus 2006 @ 18:49:
Ik ben bezig met een JSF AJAX component. Ik heb er al eerder enkele gemaakt, maar nu wil ik het extra netjes doen, namelijk door in mijn AJAX request gebruik te maken van dezelfde beans.

Het tonen van het component gaat goed, maar als ik een AJAX request doe, blijkt mijn component tree leeg te zijn. (context.getViewRoot() bevat geen children en findComponent() levert dus geen resultaat op)
Ik ben zelf ook bezig met JSF componenten te bouwen die AJAX gebruiken, en ik moet zeggen het is best wel een hoop gepriegel om het exact goed te krijgen allemaal. Als je ze eenmaal volledig werkend hebt zijn ze door andere programmeurs wel heel erg makkelijk te gebruiken.

Het is zo moeilijk te zeggen wat er bij jou fout gaat. Mischien dat de AJAX request die je doet niet als een faces request wordt herkent? Omdat te laten herkennen moet je voor myfaces als een post tenminste de de volgende parameters mee sturen: jsf_tree_64, jsf_state_64, jsf_viewid en de naam van de form waar binnen je component staat met _SUBMIT erachter. Als waardes lees je gewoon met javascript die waarden uit die in je pagina werden geschreven nadat de eerste (non-faces) request is gedaan. Voor de sun implementatie is de com.sun.faces.VIEW parameter nodig.

Ik weet niet of dit het probleem is bij jou, maar ik zal je nog even kort mijn aanpak uitleggen, mischien dat dat je weer wat in de goede richting helpt.

Wat ik doe in het jsf component is in de encodeBegin functie eerst een referentie naar het javascript (<script src="myscript.jsf"/>) schrijven wat de AJAX functionaliteit bevat. Vervolgens zet ik een boolean in de requestmap zodat ik kan checken dat ik niet 2 of meer keer per request een script referentie naar de pagina schrijf. Daarna schrijft mijn component een stukje component specificieke javascript die enkele specificieke parameters toevoegt aan een globale array die het generieke javascript defineert. Hierna volgt de gewone HTML output van het component.

Hiernaast heb ik een PhaseListener die alleen kijkt of de viewId van de viewRoot overeenkomt met de naam van het script en zo ja het (generieke) scriptje serveert en meteen response.complete oproept. De phaselistener functioneert nu eigenlijk als een simpele servlet, met als voordeel dat de user deze niet in z'n web.xml hoeft te declareren.

Het generieke script zal nu geladen worden en uitgevoerd worden via een onloadhandler die erin staat. Deze loopt de genoemde globale array af en start timers op voor alle componenten die zich aan deze array hebben toegevoegd.

AJAX requesten die gedaan worden gaan naar dezelfde url (viewid) als de pagina waar het component op staat. Behalve die genoemde params die nodig zijn om het als faces request te laten herkennen stop ik nog 2 andere parameters in de request: de ID van het component met als waarde "ajax_request" en een andere met als naam abort_jsf_req en als value true.

In de java code van het jsf component check ik nu in de decode phase (method decode(...) ) of er een parameter met m'n eigen id in de request zit en zo ja of die de waarde "ajax_request" heeft. Zoja, dan kan ik een stukje XML opbouwen en naar de response schrijven. Omdat in de decode phase geen enkel andere component naar de response schrijft (dat gebeurt normaal pas in de render phase), zal het enkele component nu als een soort mini servletje fungeren.

Op het moment dat ik hier de XML opbouw, benader ik via de properties van mijn component de beans en trek hier dus direct of indirect de benodigde waarden uit. (bv, stel ik heb een propery op mijn component die getNames() heet, dan gebruikt de getNames method de EL die naar de bean verwijst, maakt er een value binding van aan en benadert hiermee dus indirect de bean.)

Vervolgens komt de phaselistener weer kijken die na de "process validations" phase de abort_jsf_req parameter in de request checked, en als deze voorkomt (met waarde true) responseComplete() aanroept. Er zal dus nu geen enkel ander component meer iets naar de response schrijven, en het javascript dat oorspronkelijk de request deed zal alleen de XML response binnen krijgen van het betreffende component.

Dit is in een note dop ongeveer wat ik doe, en wat tot nu toe goed werkt. Het gepriegel gaat er een beetje in zitten dat de programmeur die jou componenten gebruikt, meerdere hiervan op de pagina kan zetten, zodat je je generieke javascript ook echt generiek moet houden en in je response vanuit je component alleen een response moet sturen als de request voor jou was (vandaar dat ik dus een parameter met m'n eigen ID mee stuur en het component daarop laat checken).

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Topicstarter
flowerp schreef op zondag 20 augustus 2006 @ 23:23:
[...]

Het is zo moeilijk te zeggen wat er bij jou fout gaat. Mischien dat de AJAX request die je doet niet als een faces request wordt herkent? Omdat te laten herkennen moet je voor myfaces als een post tenminste de de volgende parameters mee sturen: jsf_tree_64, jsf_state_64, jsf_viewid en de naam van de form waar binnen je component staat met _SUBMIT erachter. Als waardes lees je gewoon met javascript die waarden uit die in je pagina werden geschreven nadat de eerste (non-faces) request is gedaan. Voor de sun implementatie is de com.sun.faces.VIEW parameter nodig.
Hmm, dit probleem dacht ik eigenlijk al opgelost te hebben door een soort "initiële" request te doen. Het lijkt er echter op dat bij zo'n request de component tree pas opgebouwd wordt in de render response phase. Wat op zijn beurt ook weer logisch is, aangezien je dan pas weet welke view je rendert.

Ik denk idd dat de methode met post vars de juiste oplossing is. Dat klopt dan ook met wat ik in één of andere tutorial las, maar dat leek me in eerste instantie te omslachtig. Blijkbaar is het dus noodzakelijk.

Fat Pizza's pizza, they are big and they are cheezy


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Topicstarter
Het is idd een heel gedoe. Voor mensen die zich er nog aan willen wagen, hierbij een handig artikel hoe je het aan kunt pakken in verschillende scenario's.

https://bpcatalog.dev.java.net/ajax/jsf-ajax/

[ Voor 13% gewijzigd door JKVA op 21-08-2006 18:04 ]

Fat Pizza's pizza, they are big and they are cheezy


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Topicstarter
flowerp schreef op zondag 20 augustus 2006 @ 23:23:
[...]
Het is zo moeilijk te zeggen wat er bij jou fout gaat. Mischien dat de AJAX request die je doet niet als een faces request wordt herkent? Omdat te laten herkennen moet je voor myfaces als een post tenminste de de volgende parameters mee sturen: jsf_tree_64, jsf_state_64, jsf_viewid en de naam van de form waar binnen je component staat met _SUBMIT erachter. Als waardes lees je gewoon met javascript die waarden uit die in je pagina werden geschreven nadat de eerste (non-faces) request is gedaan. Voor de sun implementatie is de com.sun.faces.VIEW parameter nodig.
[...]
Ik merk dat ik in mijn form niet eens al die params heb staan. Zal dat kunnen komen doordat ik serverside state saving gebruik? Antwoord: Ja

De enige hidden fields die ik nu heb, zijn: _id9_SUBMIT en jsf_sequence.

JSF_viewid heb ik wel vaker gezien, maar nu ook niet eens. Niet interessant



Hahaaaaa, gelukt. De enige vereiste parameter is die <formnaam>_SUBMIT=1 parameter en hij herkent hem.
Dank aan flowerp. :)

Nog even wat dingen voor als iemand een keer zin heeft om wat coole componentjes te maken en nagels wil overhouden. :P
  • Werk niet met een aparte AJAX view ID om naar te requesten, maar gebruik een parameter, anders kan JSF bij voorbaat niet bepalen welke component tree gebruikt moet worden.
  • Geef die <formnaam>_SUBMIT=1 parameter mee in je request.
  • Ook leuk, als je meer dan één keer een request wilt doen (wat naar mijn idee de essentie is van heel AJAX) zet de volgende code vlak boven context.responseComplete();
    Java:
    1
    2
    3
    4
    5
    
        StateManager stateManager = context.getApplication().getStateManager();
        if (!stateManager.isSavingStateInClient(context))
        {
            stateManager.saveSerializedView(context);
        }

    Anders werkt het NIET (of iig niet meer dan 1x).
offtopic:
Sorry voor al die niet-edits. :)

[ Voor 33% gewijzigd door JKVA op 21-08-2006 20:13 ]

Fat Pizza's pizza, they are big and they are cheezy


  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
JKVA schreef op maandag 21 augustus 2006 @ 18:19:
[...]
Ik merk dat ik in mijn form niet eens al die params heb staan. Zal dat kunnen komen doordat ik serverside state saving gebruik? Antwoord: Ja
Je hebt het zelf al beantwoord, maar dat klopt dus ja. Ik was het er in mijn betoog ook vergeten bij te zetten. In principe is voor MyFaces bij server-side saving ook nog nodig om de jsf_sequence param over te nemen (die zou je wel in je form moeten hebben staan). Als je componenten schrijft die universeel moeten werken met zowel MyFaces als SUN RI en dan zowel state-on-client als state-on-server, schrijf je gewoon een javascriptje die in een loopje checked of de hele genoemde reeks params in de form voorkomt en zo ja ze met value en al overneemt voor de AJAX post request.

Voor JSF 1.1 zijn deze dingen helaas niet gestandaardiseerd. De spec zegt alleen dat er state gesaved moet kunnen worden client en server, maar niet -hoe-. Via hidden params ligt voor de hand, maar de naam hiervan staat dus niet vast. In JSF 1.2 (de eerste JSF die standaard in Java EE zit) zou dit allemaal wat beter geregeld moeten zijn. Ik heb hier zelf nog heel weinig naar gekeken eerlijk gezegd.

Wat trouwens ook een leuke hint is voor deze dingen; attach de source van myfaces in je favoriete IDE en step door je source heen vanaf de facesservlet. Je ziet dan precies op welke punten MyFaces beslist of iets wel of niet een faces request etc is.
De enige vereiste parameter is die <formnaam>_SUBMIT=1 parameter en hij herkent hem.
jsf_sequence -zou- toch ook nodig zijn, maar waarom die _SUBMIT nodig is, is omdat MyFaces alleen die componenten zich laat decoden die binnen de genoemde form vallen. Als je stept door de source zul je zien dat als de _SUBMIT niet gevonden wordt, Myfaces meteen de hele processing van alle child components van die form overslaat.
Dank aan flowerp. :)
you're welcome :)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.

Pagina: 1