it-swarm-fr.com

commandeButton / commandLink / ajax action / méthode du programme d'écoute non appelée ou valeur d'entrée non définie / mise à jour

Parfois, lorsque vous utilisez <h:commandLink>, <h:commandButton> ou <f:ajax>, la méthode action, actionListener ou listener associée à la balise n'est tout simplement pas appelée . Ou bien, les propriétés du bean ne sont pas mises à jour avec les valeurs soumises UIInput.

Quelles sont les causes possibles et les solutions pour cela?

329
Muhammad Hewedy

Introduction

Lorsqu'un composant UICommand (<h:commandXxx>, <p:commandXxx>, etc.) ne parvient pas à appeler la méthode d'action associée, ou un composant UIInput (<h:inputXxx>, <p:inputXxxx>, etc) ne parvient pas à traiter les valeurs soumises et/ou à mettre à jour les valeurs du modèle, et vous ne voyez aucune exception ou avertissement acceptable dans le journal du serveur, pas plus que lorsque vous configurez un gestionnaire d'exceptions ajax selon Gestion des exceptions dans les demandes ajax JSF , ni lorsque vous définissez le paramètre de contexte ci-dessous dans web.xml,

<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>

et vous ne voyez pas non plus d’erreurs ni d’avertissements dans la console JavaScript du navigateur (appuyez sur la touche F12 dans Chrome/Firefox23 +/IE9 + pour ouvrir le jeu d’outils du développeur Web, puis ouvrez la console ), parcourez ensuite la liste ci-dessous des causes possibles.

Causes possibles

  1. Les composants UICommand et UIInput doivent être placés à l'intérieur d'un composant UIForm, par ex. <h:form> (et donc pas HTML _ <form>], sinon rien ne peut être envoyé au serveur. Les composants UICommand ne doivent pas non plus avoir l'attribut type="button", sinon ce sera un bouton mort qui n'est utile que pour JavaScript onclick. Voir aussi Comment envoyer des valeurs de saisie de formulaire et appeler une méthode dans un bean JSF et <h: le bouton de commande> n'initie pas de publication .

  2. Vous ne pouvez pas imbriquer plusieurs composants UIForm l'un dans l'autre. Ceci est illégal en HTML. Le comportement du navigateur n'est pas spécifié. Méfiez-vous des fichiers inclus! Vous pouvez utiliser les composants UIForm en parallèle, mais ils ne se traiteront pas pendant la soumission. Vous devriez également faire attention avec l'anti-modèle "God Form"; assurez-vous de ne pas traiter/valider involontairement toutes les autres entrées (invisibles) dans le même formulaire (par exemple, avoir un dialogue caché avec les entrées requises dans le même formulaire). Voir aussi Comment utiliser <h: form> dans la page JSF? Formulaire unique? Formulaires multiples? Formulaires imbriqués? .

  3. Aucune erreur de validation/conversion de UIInput valeur n'aurait dû se produire. Vous pouvez utiliser <h:messages> pour afficher tous les messages qui ne sont représentés par aucun composant <h:message> spécifique à une entrée. N'oubliez pas d'inclure le id de <h:messages> dans le <f:ajax render>, le cas échéant, afin qu'il soit également mis à jour sur les requêtes ajax. Voir aussi h: messages n’affiche pas les messages lorsque p: le bouton de commande est enfoncé .

  4. Si les composants UICommand ou UIInput sont placés dans un composant itératif tel que <h:dataTable>, <ui:repeat>, etc., vous devez vous assurer que le même value du Le composant itératif est préservé pendant la phase des valeurs de la demande d’application de la demande d’envoi de formulaire. JSF réitérera dessus pour trouver le lien/bouton cliqué et les valeurs d'entrée soumises. Placer le bean dans la portée de la vue et/ou vous assurer que vous chargez le modèle de données dans @PostConstruct du bean (et donc pas dans une méthode getter!) Devrait résoudre ce problème. Voir aussi Comment et quand devrais-je charger le modèle à partir de la base de données pour h: dataTable .

  5. Si les composants UICommand ou UIInput sont inclus dans une source dynamique telle que <ui:include src="#{bean.include}">, vous devez vous assurer que la même valeur #{bean.include} est conservée pendant la durée de création de la vue de le formulaire de demande de soumission. JSF le réexécutera lors de la construction de l'arborescence des composants. Placer le bean dans la portée de la vue et/ou vous assurer que vous chargez le modèle de données dans @PostConstruct du bean (et donc pas dans une méthode getter!) Devrait résoudre ce problème. Voir aussi Comment ajax-refresh dynamique inclure le contenu par le menu de navigation? (JSF SPA) .

  6. L'attribut rendered du composant et de tous ses parents et l'attribut test de tout parent <c:if>/<c:when> ne doivent pas être évalués à false pendant la demande d'application phase des valeurs de la demande de soumission de formulaire. JSF le revérifiera dans le cadre de la protection contre les demandes altérées/piratées. Stocker les variables responsables de la condition dans un bean @ViewScoped ou veiller à la préinitialiser correctement dans @PostConstruct d'un bean @RequestScoped devrait résoudre le problème. Il en va de même pour l'attribut disabled du composant, qui ne doit pas être évalué à true pendant la phase des valeurs de la demande d'application. Voir aussi action du bouton de commande JSF non invoquée et l'envoi de formulaire dans le composant restitué sous condition n'est pas traité .

  7. L'attribut onclick du composant UICommand et l'attribut onsubmit du composant UIForm ne doivent pas renvoyer false ou provoquer une erreur JavaScript. Dans le cas de <h:commandLink> ou <f:ajax>, aucune erreur JS n'apparaît également dans la console JS du navigateur. Habituellement, googler le message d'erreur exact vous donnera déjà la réponse. Voir aussi L'ajout de jQuery aux résultats PrimeFaces donne des typesErrors non capturés .

  8. Si vous utilisez Ajax via JSF 2.x <f:ajax> ou par exemple. PrimeFaces <p:commandXxx>, assurez-vous que vous avez un <h:head> dans le modèle principal au lieu de <head>. Sinon, JSF ne pourra pas inclure automatiquement les fichiers JavaScript nécessaires contenant les fonctions Ajax. Cela entraînerait une erreur JavaScript telle que "mojarra n'est pas défini" ou "PrimeFaces n'est pas défini" dans la console JS du navigateur. Voir aussi h: commandLink actionlistener n'est pas appelé lorsqu'il est utilisé avec f: ajax et ui: repeat .

  9. Si vous utilisez Ajax et que les valeurs soumises finissent par être null, assurez-vous que les composants UIInput et UICommand qui vous intéressent sont couverts par le <f:ajax execute> ou par ex. <p:commandXxx process>, sinon ils ne seront pas exécutés/traités. Voir aussi Les valeurs de formulaire soumises ne sont pas mises à jour dans le modèle lors de l'ajout de <f: ajax> à <h: bouton de commande> et comprenant les attributs de traitement et de mise à jour de PrimeFaces et les attributs d'exécution/de rendu f: ajax de JSF = =.

  10. Si les valeurs soumises finissent toujours par être null et que vous utilisez CDI pour gérer les beans, assurez-vous d'importer l'annotation de la portée à partir du bon package, sinon CDI utilisera par défaut @Dependent, qui recrée le haricot à chaque évaluation de l'expression EL. Voir aussi le bean @ SessionScoped perd sa portée et est recréé tout le temps, les champs deviennent nuls et Quelle est la portée par défaut du bean géré dans une application JSF 2?

  11. Si un parent du <h:form> avec le bouton UICommand a été préalablement rendu/mis à jour par une requête ajax provenant d'un autre formulaire de la même page, la première action échouera toujours dans JSF version 2.2 ou antérieure. La deuxième action et les actions suivantes fonctionneront. Cela est dû à un bogue dans la gestion de l'état d'affichage qui est signalé comme problème de spécification JSF 79 et actuellement corrigé dans la version 2.3 de JSF. Pour les anciennes versions de JSF, vous devez spécifier explicitement l'ID du <h:form> dans le render du <f:ajax>. Voir aussi h: commandButton/h: commandLink ne fonctionne pas au premier clic, ne fonctionne qu'au deuxième clic .

  12. Si le <h:form> a enctype="multipart/form-data" configuré pour prendre en charge le téléchargement de fichiers, vous devez vous assurer que vous utilisez au moins JSF 2.2 ou que le filtre de servlet responsable de l'analyse multipart/form -data request est correctement configuré, sinon la FacesServlet finira par ne recevoir aucun paramètre de requête et ne pourra donc pas appliquer les valeurs de la requête. La configuration d'un tel filtre dépend du composant de téléchargement de fichier utilisé. Pour Tomahawk <t:inputFileUpload>, cochez cette réponse et pour PrimeFaces <p:fileUpload>, cochez cette réponse . Ou, si vous ne chargez pas du tout de fichier, supprimez l'attribut.

  13. Assurez-vous que l'argument ActionEvent de actionListener est un javax.faces.event.ActionEvent et donc pas Java.awt.event.ActionEvent, ce que la plupart des IDE suggèrent comme première option d'auto-complétion. Ne pas avoir d'argument est également faux si vous utilisez actionListener="#{bean.method}". Si vous ne voulez pas d'argument dans votre méthode, utilisez actionListener="#{bean.method()}". Ou peut-être voulez-vous réellement utiliser action au lieu de actionListener. Voir aussi Différences entre action et actionListener .

  14. Assurez-vous que ni PhaseListener ni aucun EventListener de la chaîne requête-réponse n'a modifié le cycle de vie JSF pour ignorer la phase d'action d'appel, par exemple en appelant FacesContext#renderResponse() ou FacesContext#responseComplete().

  15. Assurez-vous que ni Filter ni Servlet dans la même chaîne de requête-réponse n'a bloqué la requête de FacesServlet d'une manière ou d'une autre.

  16. Si vous utilisez un PrimeFaces <p:dialog> ou un <p:overlayPanel>, assurez-vous qu'ils possèdent leur propre <h:form>. Parce que, par défaut, ces composants sont déplacés par JavaScript vers la fin de HTML <body>. Ainsi, s’ils étaient initialement assis dans un <form>, ils ne seraient plus assis dans un <form>. Voir aussi p: l'action du bouton de commande ne fonctionne pas dans p: dialogue

  17. Bug dans le cadre. Par exemple, RichFaces a un " erreur de conversion " si vous utilisez un élément rich:calendar UI avec un attribut defaultLabel (ou, dans certains cas, un rich:placeholder élément). Ce bogue empêche l'appel de la méthode bean quand aucune valeur n'est définie pour la date du calendrier. Le traçage des bogues de l’infrastructure peut être accompli en commençant par un exemple simple et en construisant la page jusqu’à sa découverte.

Astuces de débogage

Si vous êtes toujours bloqué, il est temps de déboguer. Du côté client, appuyez sur F12 dans le navigateur Web pour ouvrir le jeu d’outils de développement Web. Cliquez sur l'onglet Console afin d'afficher le code JavaScript. Il devrait être exempt d'erreurs JavaScript. Au-dessous de la capture d'écran, vous trouverez un exemple tiré de Chrome, qui illustre le cas de la soumission d'un bouton activé <f:ajax> sans avoir déclaré <h:head> (comme décrit au point 7 ci-dessus).

js console

Cliquez sur l'onglet Réseau pour afficher le moniteur de trafic HTTP. Soumettez le formulaire et déterminez si les en-têtes de requête, les données du formulaire et le corps de la réponse sont conformes aux attentes. Sur la capture d'écran ci-dessous, vous trouverez un exemple tiré de Chrome, qui illustre la bonne façon de soumettre un formulaire simple avec un seul <h:inputText> et un seul <h:commandButton> avec <f:ajax execute="@form" render="@form">.

network monitor

(avertissement: lorsque vous publiez des captures d'écran des en-têtes de requêtes HTTP comme ci-dessus dans un environnement de production, assurez-vous de brouiller/masquer tous les cookies de session de la capture d'écran pour éviter les attaques de détournement de session!)

Côté serveur, assurez-vous que ce serveur est démarré en mode débogage. Placez un point d'arrêt de débogage dans une méthode du composant JSF d'intérêt que vous comptez appeler lors du traitement de l'envoi du formulaire. Par exemple. dans le cas du composant UICommand, ce serait UICommand#queueEvent() et dans le cas du composant UIInput, ce serait UIInput#validate() . Il suffit de suivre l'exécution du code et de vérifier si le flux et les variables sont conformes aux attentes. La capture d'écran ci-dessous est un exemple tiré du débogueur d'Eclipse.

debug server

661
BalusC

Si votre h:commandLink est dans un h:dataTable, il y a une autre raison pour laquelle le h:commandLink risque de ne pas fonctionner:

La source de données sous-jacente liée au h:dataTable doit également être disponible dans le deuxième cycle de vie JSF déclenché lorsque l'utilisateur clique sur le lien.

Donc, si la demande de la source de données sous-jacente est limitée, le h:commandLink ne fonctionne pas!

52
jbandi

Bien que ma réponse ne soit pas applicable à 100%, mais la plupart des moteurs de recherche estiment qu'il s'agit du premier coup, j'ai décidé de le publier néanmoins:

Si vous utilisez PrimeFaces (ou une API similaire) p:commandButton ou p:commandLink, il y a des chances que vous ayez oublié d'ajouter explicitement process="@this" à vos composants de commande.

Comme indiqué dans la section 3.18 du Guide de l'utilisateur PrimeFaces, les valeurs par défaut pour process et update sont toutes deux @form, ce qui est à peu près opposé aux valeurs par défaut que vous pouvez attendre de JSF f:ajax ou RichFaces, qui sont respectivement execute="@this" et render="@none".

Je viens de prendre un looong temps pour le découvrir. (... et je pense que c'est plutôt rare d'utiliser des valeurs par défaut différentes de JSF!)

28
Kawu

Je mentionnerai encore une chose qui concerne le p:commandButton de Primefaces _!

Lorsque vous utilisez un p:commandButton pour l’action à exécuter sur le serveur, vous ne pouvez pas utiliser type="button" car il s’agit de Boutons poussoir qui sont utilisés pour exécuter du javascript personnalisé sans provoquer de requête ajax/non-ajax au serveur.

À cette fin, vous pouvez utiliser l'attribut type (la valeur par défaut est "submit") ou vous pouvez utiliser explicitement type="submit".

J'espère que cela aidera quelqu'un!

9
akelec

J'ai récemment rencontré un problème avec une UICommand qui n'appelle pas dans une application JSF 1.2 utilisant IBM Extended Faces Components.

J'avais un bouton de commande sur une ligne d'un datatable (la version étendue, donc <hx:datatable>) et l'UICommand ne se déclencherait pas à partir de certaines lignes de la table (les lignes qui ne se déclencheraient pas étaient supérieures à la ligne par défaut taille d'affichage).

J'ai eu un composant déroulant pour sélectionner le nombre de lignes à afficher. La valeur sauvegardant ce champ était dans RequestScope. Les données sauvegardant la table elle-même étaient dans une sorte de ViewScope (en réalité, temporairement dans SessionScope).

Si l'affichage augmentait le nombre de lignes via le contrôle dont la valeur était également liée à l'attribut rows du datatable, aucune des lignes affichées à la suite de cette modification ne pourrait déclencher l'UICommand lorsque l'utilisateur clique dessus.

En plaçant cet attribut dans la même portée que les données de la table, le problème a été résolu.

Je pense que cela est mentionné dans BalusC # 4 ci-dessus, mais non seulement la valeur de la table doit être étendue à View ou Session, mais également l'attribut contrôlant le nombre de lignes à afficher sur cette table.

3
Brent C

Je me suis coincé avec ce problème moi-même et j'ai trouvé une autre cause à ce problème. Si vous n'avez pas de méthodes de définition dans votre bean de support pour les propriétés utilisées dans votre * .xhtml, l'action n'est tout simplement pas appelée.

3
Syed Mehtab

J'ai eu ce problème aussi et seulement vraiment commencé à affiner la cause après avoir ouvert la console Web du navigateur. Jusque-là, je ne pouvais pas obtenir de message d'erreur (même avec <p:messages>). La console Web affiche un code d’état HTTP 405 provenant de <h:commandButton type="submit" action="#{myBean.submit}">.

Dans mon cas, j'ai un mélange de Vanilla HttpServlet fournissant OAuth authentification via Auth0 et des facelets JSF et des beans exécutant mes vues d'application et ma logique métier.

Une fois que j'ai refactoré mon fichier web.xml et enlevé un servlet intermédiaire, il a ensuite "magiquement" fonctionné.

En fin de compte, le problème était que middle-man-servlet utilisait RequestDispatcher.forward (...) pour rediriger de l'environnement HttpServlet vers l'environnement JSF, tandis que le servlet appelé avant était redirigé avec HttpServletResponse.sendRedirect (.. .).

Fondamentalement, l’utilisation de sendRedirect () permettait au "conteneur" JSF de prendre le contrôle alors que RequestDispatcher.forward () n’était évidemment pas.

Ce que je ne sais pas, c'est pourquoi le facelet a pu accéder aux propriétés du haricot, mais ne peut pas les définir, et cela crie clairement pour avoir supprimé le mélange de servlets et de JSF, mais j'espère que cela aidera quelqu'un à éviter de nombreuses heures de travail en tête. frapper à table.

2
Brooks

Une possibilité supplémentaire: si le symptôme est que la première invocation fonctionne, mais pas les suivantes, vous pouvez utiliser PrimeFaces 3.x avec JSF 2.2, comme détaillé ici: aucun ViewState n'est envoyé .

0
Dave Mulligan

J'ai eu beaucoup de plaisir à déboguer un problème où l'action de <h:commandLink> dans richfacesdatatable refusait de se déclencher. La table fonctionnait à un moment donné mais s’arrêtait sans raison apparente. Je n’ai laissé aucune pierre non retournée, seulement pour découvrir que mon rich:datatable utilisait le mauvais rowKeyConverter qui retournait des nulls que richfaces utilisait avec bonheur comme clés de rangée. Cela a empêché que mon action <h:commandLink> soit appelée.

0
Mustafa