JSF 2: Problema com required=true no h:inputText !  XML
Índice dos Fóruns » Desenvolvimento Web
Autor Mensagem
nei.junior
JavaBaby
[Avatar]
Membro desde: 11/12/2008 15:57:03
Mensagens: 98
Localização: SP
Offline

Pessoal, boa tarde !

Estou com um problema no "h:inputText" com atributo "required=true".
Para entender minha pergunta no final, considere o seguinte exemplo:
Obs.: Segue abaixo um link com um exemplo montado para testarem também, porém vou explicar:

http://www.mandamais.com.br/download/8beq2542011165533

Ao executar o exemplo, irá abrir uma grid com duas colunas preenchidas da seguinte forma:

Agora siga os seguintes passos:

* Cliquei no "Editar" do nome JOÃO e repare que na próxima tela aparecerá assim:

* Feito isso clique em voltar;
* Cliquei no "Editar" do nome MARIA e repare que na próxima tela aparecerá assim:

* Feito isso clique em voltar;
* Depois cliquei novamente no "Editar" do nome JOÃO;
* Na próxima tela apague input nome ("JOÃO nome") deixando em branco e clique em "Atualizar e Voltar". Observe que no input nome aparecerá uma mensagem de "Campo Obrigatório";
* Clique no botão "Voltar";
* Na grid clique no "Editar" do nome "MARIA nome";
* Repare que na próxima tela aparecerá aparecerá da seguinte forma:

* Obs.: O correto era aparecer da seguinte forma:

Minha pergunta é: Porque isso acontece quando tenho um input que é requerido na tela ?
O correto não era ignorar a execução do botão "Atualizar e Voltar" que quando clico em "Voltar" e quando "Editar" novamente entrar com os valores correspondentes ao objeto que selecionei ?

Obs.: Não quero saber como contornar essa questão, isso eu já sei, quero saber por que ocorre isso neste caso.
Isso é um comportamento correto do JSF ou não ?
Eu estou fazendo algum procedimento errado ? Se sim, qual seria a forma correta neste caso ?

This message was edited 2 times. Last update was at 25/04/2011 20:18:57


Mojarra 2.1.6, Facelets, Glassfish V3.1.1, EclipseLink 2.2, Primefaces 3.1, NetBeans 7.1.

Nei Alcantara Jr.
http://twitter.com/NeiAlcantaraJr

"Tudo aquilo que algum idiota diz que é urgente, é algo que um imbecil não fez em tempo hábil e quer que você se ferre para fazer em tempo recorde."
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

Olá nei.junior,

Antes de mais nada, esse problema não está relacionado somente a validação "required", mas sim qualquer erro na fase de validação (seja conversação ou validação).

Vou tentar explicar de maneira simples, o problema e as possíveis soluções que eu conheço.

Os componentes de inputs (todos que implementam EditableValueHolder) possuem 3 (três) tipos de valores que são alterados durante o ciclo de vida de uma requisição, eles são:
1) Submitted Value
2) Local Value
3) Value Binding (aka model value)

Vamos entender agora como esses 3 valores mudam nos componentes durante o ciclo de vida:

** Quando você submete um formulário todos os inputs da página são submetidos como String (parâmetros de request) e na APPLY REQUEST VALUES (2a fase) este valores são setados nos seus devidos componentes de inputs como "submitted value";

** Após isso, na fase de validação (3a fase), cada componente de input tenta converter e validar seu submitted value, em caso de sucesso o componente define seu novo valor convertido como "local value" e seta para null seu submitted value, continuando assim o ciclo de vida. Em caso de erro de conversão ou validação o componente não define seu "local value" e marca o componente como invalido, que por fim pula para última fase (RENDER RESPONSE);

** Se não houver erro na fase de validação o ciclo de vida continua na UPDATE MODEL VALUES para popular o modelo (managed bean etc), para cada componente de input setado no modelo o seu "local value" é setado para null e o ciclo termina na RENDER RESPONSE exibindo os valores do modelo (através das EL's);

O "problema" realmente ocorre na RENDER RESPONSE porque o componente pode retornar qualquer um dos 3 valores, contudo seguindo essa ordem de prioridade:

** Se houver submitted value então retorne-o;
** Caso contrário, se o local value é não nulo então retorne-o;
** Caso contrário, avalie a EL, ou seja, chame o getter do modelo;

Entendendo essa prioridade acima você mata a charada do problema

Esse problema ocorre muito comumente quando trabalhamos com a mesma árvore de componentes, ou seja, quando utilizamos muito AJAX e/ou navegação orientada a estados. Quando utilizamos a navegação padrão do JSF dificilmente caímos nesse problema.

Todas as soluções que conheço envolve limpar a árvore de componentes que está "suja":

1) Navegação padrão do Faces para recriar toda a viewroot (não vale retornar null);
2) Limpar de maneira educada todos os componentes de inputs;
3) Limpar os inputs de maneira "brute force" (parentComponent.getChildren().clear() - eles serão recriados na RENDER RESPONSE novamente, porém só funciona com JSF 1.x, pois o 2.x parece seguir corretamente a spec);

Eu acho que acabei falando de mais, isso daria até um post no blog, mas a preguiça nunca me deixou escrever, rss.

No mais, segue alguns links para complementar o que eu disse,
http://cagataycivici.wordpress.com/2005/12/28/jsf_component_s_value_local/
http://ovaraksin.blogspot.com/2010/06/submitted-value-local-value-model-value.html
http://stackoverflow.com/questions/260094/jsf-getvalue-v-s-getsubmittedvalue

Um abraço e espero que tenha ficado claro!

Rafael Ponte
http://www.rponte.com.br/
[WWW]
Paulo Silveira
Administrador
[Avatar]

Membro desde: 07/08/2002 18:38:50
Mensagens: 4158
Localização: São Paulo
Offline

Viveriamos melhor se todos nos conhecessemos tanto de JSF quanto o Ponte!

http://blog.caelum.com.br twitter: @paulo_caelum


[Email] [WWW]
vinnysoft
JavaTeenager
[Avatar]

Membro desde: 21/09/2010 00:56:24
Mensagens: 191
Localização: Vitória - Espírito Santo
Offline

Quando eu crescer quero ser igual ao Ponte e ao VinyGodoi!



danilo.magrini
What is classpath?

Membro desde: 26/04/2011 08:09:57
Mensagens: 8
Offline

** Após isso, na fase de validação (3a fase), cada componente de input tenta converter e validar seu submitted value, em caso de sucesso o componente define seu novo valor convertido como "local value" e seta para null seu submitted value, continuando assim o ciclo de vida. Em caso de erro de conversão ou validação o componente não define seu "local value" e marca o componente como invalido, que por fim pula para última fase (RENDER RESPONSE);

** Se não houver erro na fase de validação o ciclo de vida continua na UPDATE MODEL VALUES para popular o modelo (managed bean etc), para cada componente de input setado no modelo o seu "local value" é setado para null e o ciclo termina na RENDER RESPONSE exibindo os valores do modelo (através das EL's);


Só fazendo uma observação aqui Rafael, o local value é setado logo após o submitted value ser convertido (se existir um converter associado) e sequer atinge o estágio de validação pois se assim fosse o local value nunca serviria pra nada já que se a conversão foi correta e a validação também então o modelo já seria atualizado e o local value passaria para null correto?
nei.junior
JavaBaby
[Avatar]
Membro desde: 11/12/2008 15:57:03
Mensagens: 98
Localização: SP
Offline

Bom dia a todos !

Rafael, primeiramente quero agradecer pela resposta, foi bastante esclarecedora.
Fazendo alguns testes aqui com o @DaniloMagrini, achamos um procedimento um pouco estranho.
Vou exemplificar o procedimento que fiz primeiramente para depois perguntar:

* No setCliente do "mbTesteRequired" e no método "editar" fiz o seguinte procedimento:

* Levando em consideração o procedimento que ja expliquei acima, onde clico para editar o "JOÃO", deixo em branco o nome e clico em "Atualizar e voltar" para dar "Campo Obrigatório", depois clico no botão "Voltar", e por fim na grid clico em editar "MARIA" e la na tela de cadastro vem como:

* Quando pego o que imprimiu no "output" do netBeans esta assim:

* Agora no momento que cliquei no botão "Editar" do nome "MARIA" ele atualizou o modelo, significa que ele executou todos os outros processos corretamente, ou seja, o "Submitted Value" e o "Local Value" estão como "NULL".
Agora porque que na tela continua errado ? Não era para vir certo ?

Obrigado por enquanto !


Mojarra 2.1.6, Facelets, Glassfish V3.1.1, EclipseLink 2.2, Primefaces 3.1, NetBeans 7.1.

Nei Alcantara Jr.
http://twitter.com/NeiAlcantaraJr

"Tudo aquilo que algum idiota diz que é urgente, é algo que um imbecil não fez em tempo hábil e quer que você se ferre para fazer em tempo recorde."
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

Oi Danilo,

danilo.magrini wrote:
Só fazendo uma observação aqui Rafael, o local value é setado logo após o submitted value ser convertido (se existir um converter associado) e sequer atinge o estágio de validação pois se assim fosse o local value nunca serviria pra nada já que se a conversão foi correta e a validação também então o modelo já seria atualizado e o local value passaria para null correto?


Na verdade eu gostaria de garantir isso para você, mas já li artigos que afirmam que o local value é setado após a validação e outros dizem que é setado logo após a conversão, na qual a validação só cuidaria de invalidar o componente. Mas no geral, para nós "usuários" do framework que não criamos componentes não faz tanta diferença assim. No mais, o local value também é utilizado para eventos que ocorrem imediatamente após a conversão e validação dentro da própria fase de validação, como por exemplo valueChangeListener's.

Rafael Ponte
http://www.rponte.com.br/
[WWW]
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

nei.junior wrote:
* Agora no momento que cliquei no botão "Editar" do nome "MARIA" ele atualizou o modelo, significa que ele executou todos os outros processos corretamente, ou seja, o "Submitted Value" e o "Local Value" estão como "NULL".
Agora porque que na tela continua errado ? Não era para vir certo ?

Obrigado por enquanto !



Nei,

Lembre-se que a árvore de componentes ficou "suja" após o erro de validação (mas especificamente os inputs do formulário), e o que você fez após clicar no botão "Voltar" foi apenas esconder o formulário (rendered=false). O formulário, independente de quantas requisições AJAX ocorram, ainda permanece "sujo" em memoria e seus componentes de inputs não são processados no ciclo de vida pois o componente parent está rendered=false, por isso os valores internos não mudam (que já estavam "sujos" com o local value antigo, caindo naquela ordem de prioridade do valores: local value antes do model value).

Não sei se ficou claro. Caso tenha ficado confuso é só falar que tento explicar melhor.

Um abraço.

Rafael Ponte
http://www.rponte.com.br/
[WWW]
nei.junior
JavaBaby
[Avatar]
Membro desde: 11/12/2008 15:57:03
Mensagens: 98
Localização: SP
Offline

rponte wrote:
Nei,

Lembre-se que a árvore de componentes ficou "suja" após o erro de validação (mas especificamente os inputs do formulário), e o que você fez após clicar no botão "Voltar" foi apenas esconder o formulário (rendered=false). O formulário, independente de quantas requisições AJAX ocorram, ainda permanece "sujo" em memoria e seus componentes de inputs não são processados no ciclo de vida pois o componente parent está rendered=false, por isso os valores internos não mudam (que já estavam "sujos" com o local value antigo, caindo naquela ordem de prioridade do valores: local value antes do model value).

Rafael,

Entendi sim, porém essa questão do AJAX foi o que pensei por priemiro. Portanto nesse exemplo que criei, não sei se você baixou ai, mas se sim pode observar que eu NÃO utilizo AJAX, é tudo requisição normal para recarregar a página toda e mesmo assim continua errado.
Como não é AJAX ele recarrega toda a página novamente, correto ? Com isso ele deveria reconstruir tudo novamente e vir correto, não é ?

Obrigado por enquanto !

Abraço.

Mojarra 2.1.6, Facelets, Glassfish V3.1.1, EclipseLink 2.2, Primefaces 3.1, NetBeans 7.1.

Nei Alcantara Jr.
http://twitter.com/NeiAlcantaraJr

"Tudo aquilo que algum idiota diz que é urgente, é algo que um imbecil não fez em tempo hábil e quer que você se ferre para fazer em tempo recorde."
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

nei.junior wrote:
Rafael,

Entendi sim, porém essa questão do AJAX foi o que pensei por priemiro. Portanto nesse exemplo que criei, não sei se você baixou ai, mas se sim pode observar que eu NÃO utilizo AJAX, é tudo requisição normal para recarregar a página toda e mesmo assim continua errado.
Como não é AJAX ele recarrega toda a página novamente, correto ? Com isso ele deveria reconstruir tudo novamente e vir correto, não é ?

Obrigado por enquanto !

Abraço.


Nei,

Eu havia baixado sim seu código. Quando comentei sobre o AJAX foi somente para reforçar, pois normalmente o problema ocorre quando utilizamos muito AJAX. Mas como eu havia dito, seja requisição comum ou AJAX, você continua trabalhando com a mesma árvore de componentes. No seu método editar() você retorna null como regra de navegação, simbolizando para o faces navegar para a mesma página, porém utilizando a mesma árvore de componentes em vez de recriar uma nova árvore.

O problema real está na árvore de componentes "suja". Nas soluções que eu havia dado mais acima eu comentei sobre utilizar a navegação do faces, mas sem retornar null.


Rafael Ponte
http://www.rponte.com.br/
[WWW]
nei.junior
JavaBaby
[Avatar]
Membro desde: 11/12/2008 15:57:03
Mensagens: 98
Localização: SP
Offline

rponte wrote:
Nei,

Eu havia baixado sim seu código. Quando comentei sobre o AJAX foi somente para reforçar, pois normalmente o problema ocorre quando utilizamos muito AJAX. Mas como eu havia dito, seja requisição comum ou AJAX, você continua trabalhando com a mesma árvore de componentes. No seu método editar() você retorna null como regra de navegação, simbolizando para o faces navegar para a mesma página, porém utilizando a mesma árvore de componentes em vez de recriar uma nova árvore.

O problema real está na árvore de componentes "suja". Nas soluções que eu havia dado mais acima eu comentei sobre utilizar a navegação do faces, mas sem retornar null.


Rafael,

Bom concluindo o assunto, entendi. Você deu uma boa clareada neste caso.
Agora vamos ver uma das soluções postada no inicio por você.
O @DaniloMagrini deu uma ideia de propor na lista oficial do Mojarra a criação de algo como:


O que acha ? Acho que quando cair nessa situação, pelo menos teria uma forma mais simples e objetiva de contornar.


Mojarra 2.1.6, Facelets, Glassfish V3.1.1, EclipseLink 2.2, Primefaces 3.1, NetBeans 7.1.

Nei Alcantara Jr.
http://twitter.com/NeiAlcantaraJr

"Tudo aquilo que algum idiota diz que é urgente, é algo que um imbecil não fez em tempo hábil e quer que você se ferre para fazer em tempo recorde."
danilo.magrini
What is classpath?

Membro desde: 26/04/2011 08:09:57
Mensagens: 8
Offline

rponte wrote:
Na verdade eu gostaria de garantir isso para você, mas já li artigos que afirmam que o local value é setado após a validação e outros dizem que é setado logo após a conversão, na qual a validação só cuidaria de invalidar o componente. Mas no geral, para nós "usuários" do framework que não criamos componentes não faz tanta diferença assim. No mais, o local value também é utilizado para eventos que ocorrem imediatamente após a conversão e validação dentro da própria fase de validação, como por exemplo valueChangeListener's.


O ideal é procurar isso na especificação. Assim que eu tiver um tempo vou vasculhar entre as JSRs 314, 127 e 252 e ver se encontro algo. Porém se fosse para apostar eu apostaria que ele é setado depois da conversão porque pra mim setar o local value depois da validação se tornaria desnecessário uma vez que ele seria setado e logo em seguida receberia null já que o modelo seria atualizado.
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

danilo.magrini wrote:
O ideal é procurar isso na especificação. Assim que eu tiver um tempo vou vasculhar entre as JSRs 314, 127 e 252 e ver se encontro algo. Porém se fosse para apostar eu apostaria que ele é setado depois da conversão porque pra mim setar o local value depois da validação se tornaria desnecessário uma vez que ele seria setado e logo em seguida receberia null já que o modelo seria atualizado.


Mas é como eu te disse, Danilo, após a validação, dentro da PROCESS VALIDATION, ainda podem ocorrer diversos eventos antes da atualização do modelo. Nesse curto intervalo o modelo precisa estar integro para que alguns eventos funcionem corretamente, como o valueChangeEvent, por exemplo.

Rafael Ponte
http://www.rponte.com.br/
[WWW]
rponte
JavaEvangelist
[Avatar]

Membro desde: 18/02/2008 10:06:25
Mensagens: 412
Offline

nei.junior wrote:Rafael,

Bom concluindo o assunto, entendi. Você deu uma boa clareada neste caso.
Agora vamos ver uma das soluções postada no inicio por você.
O @DaniloMagrini deu uma ideia de propor na lista oficial do Mojarra a criação de algo como:


O que acha ? Acho que quando cair nessa situação, pelo menos teria uma forma mais simples e objetiva de contornar.



Nei,

Antes de mais nada, é importante definir bem o que "invalidar" significa. Significa recriar toda a árvore de componentes? Limpar o estado interno de todos os inputs ou todos os componentes?

O pessoal do Trinidad criou algo semelhante, mas acho que somente para inputs, não me recordo. Tanto que foi incorporado ao JSF 2.x. Se eu pesquisar talvez até ache o artigo.

No mais, eu acredito que você não queira invalidar toda a árvore de componentes, mas sim alguns componentes ou grupo de componentes. Então utilizar navegação é a solução mais simples e o próprio faces já te fornecesse isso.

Enfim, provavelmente há uma forte razão para o ciclo de vida e os componentes trabalharem assim! Se você souber de algo no forum não deixe de me informar.

Rafael Ponte
http://www.rponte.com.br/
[WWW]
nei.junior
JavaBaby
[Avatar]
Membro desde: 11/12/2008 15:57:03
Mensagens: 98
Localização: SP
Offline

rponte wrote:
Nei,

Antes de mais nada, é importante definir bem o que "invalidar" significa. Significa recriar toda a árvore de componentes? Limpar o estado interno de todos os inputs ou todos os componentes?

O pessoal do Trinidad criou algo semelhante, mas acho que somente para inputs, não me recordo. Tanto que foi incorporado ao JSF 2.x. Se eu pesquisar talvez até ache o artigo.

No mais, eu acredito que você não queira invalidar toda a árvore de componentes, mas sim alguns componentes ou grupo de componentes. Então utilizar navegação é a solução mais simples e o próprio faces já te fornecesse isso.

Enfim, provavelmente há uma forte razão para o ciclo de vida e os componentes trabalharem assim! Se você souber de algo no forum não deixe de me informar.

Rafael,

O invalidar seria limpar o estado interno de todos os inputs, praticamente o que você fez nessa solução.

Quanto a navegação do faces, no caso desse exemplo se ao entrar na grid eu apertasse um botão filtrar e carregar a grid, ai quando entrasse na página de cadastro e voltasse ele perderia os dados da grid pois recriou toda a arvore novamente. Isso é um exemplo simples de como colocar a navegação do faces atrapalharia (de uma certa forma) se quero manter a grid sem ter que filtrar novamente.
Portanto deixando o voltar como NULL ele não recria e não perde. Sendo assim, usando a sua solução ele funcionaria perfeitamente.

Enfim, agora que ficou claro vamos elaborar a melhor forma de trabalhar para resolver isso.
Quero agradecer pela atenção e pelas dúvidas esclarecidas e se eu souber de algo ou achar alguma outra solução, informo todos aqui.

Muito obrigado

Abraço !


Mojarra 2.1.6, Facelets, Glassfish V3.1.1, EclipseLink 2.2, Primefaces 3.1, NetBeans 7.1.

Nei Alcantara Jr.
http://twitter.com/NeiAlcantaraJr

"Tudo aquilo que algum idiota diz que é urgente, é algo que um imbecil não fez em tempo hábil e quer que você se ferre para fazer em tempo recorde."
 
Índice dos Fóruns » Desenvolvimento Web
Ir para:   
Powered by JForum 2.1.8 © JForum Team