Le guide Oracle Forms 9i/10g


précédentsommairesuivant

XXXV. Cas d'école, trucs et astuces

Navigation et ordre des items

Par défaut, la navigation à l'intérieur des blocs et des items s'effectue dans l'ordre établi dans le navigateur d'objets au moment de la conception.

Au chargement de la forme, le focus est placé sur le premier item visible et activé du premier bloc.
A l'intérieur du bloc, la navigation (au clavier) tient compte de l'ordre de création des items dans le bloc.
Lorsque l'utilisateur se déplace sur l'item suivant avec la touche Tab, le focus est placé sur l'item qui suit dans le bloc.
Si l'ordre de la conception ne vous convient pas, vous disposez de plusieurs manières pour changer cet ordre:

Réorganiser l'ordre des items dans le navigateur d'objets

Lorsque vous êtes dans le navigateur d'objets, vous pouvez (a l'intérieur d'un même conteneur) déplacer les items avec la souris (cliquer/déplacer).
Vous pouvez donc modifier l'ordre des items dans un bloc ainsi que l'ordre des blocs eux-mêmes.

Attention:
l'ordre des blocs dans le navigateur d'objets est très important.
C'est dans cet ordre que la navigation s'effectue mais également la validation.
Lors de la phase d'enregistrement (Commit) les blocs sont validés (Insert, Update, Delete) dans l'ordre donné à la conception.
Cet ordre peut être important lorsqu'il existe une relation entre les blocs.
Par exemple, si le bloc détail et situé avant le bloc maître, cela provoquera des erreurs car les enregistrements du bloc détail seront enregistés en base alors que l'enregistrement parent ne l'est pas encore.

Modifier les propriétés des objets

Plusieurs propriétés de niveau forme, bloc ou item permettent de modifier la navigation standard.

Au niveau du module, vous pouvez spécifier le nom de bloc de départ dans la propriété : Navigation -> Premier bloc de données

au niveau bloc, vous pouvez préciser quels seront les blocs suivants et précedents dans les propriétés:
Navigation -> Bloc de données de navigation précédent
Navigation -> Bloc de données de navigation suivant

Enfin, au niveau de chaque item d'un bloc, vous pouvez spécifier quels seront les items suivant et précedent lorsque l'utilisateur pressera la touche Tab ou Shift+Tab
Navigation -> Elément de navigation précédent
Navigation -> Elément de navigation suivant


Ces propriétés peuvent également être modifiées à l'exécution:

Set_Form_Property( 'nom_forme' | id_forme, FIRST_NAVIGATION_BLOCK,'nom_bloc' ) ;
Set_Block_Property( 'nom_bloc' | id_bloc , PREVIOUS_NAVIGATION_BLOCK,'nom_bloc_precedent' ) ;
Set_Block_Property( 'nom_bloc' | id_bloc , NEXT_NAVIGATION_BLOCK,'nom_bloc_suivant' ) ;
Set_Item_Property( 'nom_item' | id_item , PREVIOUS_NAVIGATION_ITEM, 'nom_item_precedent' ) ;
Set_Item_Property( 'nom_item' | id_item , NEXT_NAVIGATION_ITEM, 'nom_item_suivant' ) ;

Ces instructions sont pratiques pour respecter certains principes de navigation.

Exemple:
Dans un bloc de saisie d'état civil, la navigation est différente selon le sexe de l'individu:

 
Sélectionnez

If EMP.SEXE = 'F' Then
   Set_Item_Property( 'EMP.Prenom', NEXT_NAVIGATION_ITEM, 'EMP.Nom_Jeune_Fille' ) ;
   Set_Item_Property( 'EMP.Adresse', PREVIOUS_NAVIGATION_ITEM, 'EMP.Nom_Jeune_Fille' ) ;   
Else
   Set_Item_Property( 'EMP.Prenom', NEXT_NAVIGATION_ITEM, 'EMP.Adresse' ) ;
   Set_Item_Property( 'EMP.Adresse', PREVIOUS_NAVIGATION_ITEM, 'EMP.Prenom' ) ;      
End if ;

La navigation vers le bloc suivant peut également dépendre de règles fonctionnelles:

 
Sélectionnez

If EMP.Deptno = 10 Then
   Set_Block_Property( 'EMP', NEXT_NAVIGATION_BLOCK, 'BLOC1' ) ;
Else
   Set_Block_Property( 'EMP', NEXT_NAVIGATION_BLOCK, 'BLOC2' ) ;
End if ;



Processus de validation

Au niveau d'une forme, plusieurs niveaux de validation sont permis.

  • Forme
  • Bloc
  • Enregistrement
  • Item

Une validation intervient lorsqu'un certain niveau de modification est atteint.
Si la validation est positionnée sur la valeur Item, la validation interviendra chaque fois que celui-ci est modifié
Si elle est positionnée à Enregistrement, elle ne s'effectuera que lors d'un changement d'enregistrement.

Cela permet de régler finement le niveau de validation.
Une validation de niveau Item permet de s'assurer immédiatement de la validité de celui-ci.
A l'extrême, une validation de niveau Forme n'enclenche les processus de validation sur chaque items, puis chaque enregistrements et enfin chaque blocs qu'au momment de l'enregistrement (Commit).

Vous devez donc déterminer (si les spécifications ne le précisent pas) a quel moment s'effectueront les validations.

Réglage du niveau de validation

Il s'effectue au niveau de la propriété du module Base de données -> Unité de validation

Cette propriété peut être modifiée à l'exécution via la fonction:
Set_Form_Property( 'nom_forme' | id_form, VALIDATION_UNIT, unite_validation ) ;

unite_validation peut prendre l'une des valeurs suivantes:

  • DEFAULT_SCOPE (par défaut Item)
  • FORM_SCOPE
  • BLOCK_SCOPE
  • RECORD_SCOPE
  • ITEM_SCOPE

Quand la validation se déclenche t-elle ?

Le processus de validation se déclenche lorsque le focus quitte l'élément de validation choisi
Dès la sortie d'un item si VALIDATION_UNIT vaut ITEM_SCOPE, dès la sortie d'un enregistrement si VALIDATION_UNIT vaut RECORD_SCOPE, etc.

Au niveau Item, le processus de validation tient compte de certaines propriétés comme:

  • Obligatoire
  • Masque de format
  • Valeur minimum
  • Valeur maximum

Remarque:
Si la propriété du module Fonctionnel -> Différer mise en vigeur obligatoire est positionnée à Oui, le contrôle d'existance de valeur sur les items obligatoires ne se fera qu'à la validation de l'enregistrement.
Cette propriété peut être modifiée à l'exécution avec la fonction:
Set_Form_Property( 'nom_forme' | id_forme, DEFER_REQUIRED_ENFORCEMENT, PROPERTY_TRUE | PROPERTY_FALSE ) ;


Utilisation des temporisateurs (Timers)

Un temporisateur est une sorte de « réveil » qui se déclenche à une intervalle fixée lors de sa création.
Cela permet d'effectuer une action particulière toute les n millisecondes.

Création d'un timer

id_timer := Create_Timer( 'nom_timer', millisecondes, repetition ) ;

id_timer est une variable de type TIMER
nom_timer représente le nom du timer (30 caractères maxi)
millisecondes désigne le nombre de millisecondes (> 0) entre chaque déclenchement (de 1 à 2147483648)
repetition indique si le déclenchement aura lieu une seule fois (NO_REPEAT) ou sera répétitif (REPEAT)

Exemple de création d'un timer:

Create_Timer()
Sélectionnez

Declare
  Timer_id TIMER ;
  LI$Duree PLS_INTEGER := 30000 ; -- 30 secondes
Begin
  Timer_id := Create_Timer( 'tempo_1', LI$Duree, REPEAT ) ;
End ;


Lorsque vous créer un timer, vous devez absolument créer un déclencheur de niveau forme : When-Timer-Expired
c'est dans ce déclencheur que vous effectuerez l'action adéquate:

When-Timer-Expired
Sélectionnez

Declare
  LC$Timer VARCHAR2(30) := Get_Application_Property( TIMER_NAME ) ;
  Timer_id TIMER ;
  LC$Bloc  VARCHAR2(30) := :SYSTEM.CURSOR_BLOCK ;
Begin
  If LC$Timer = 'Tempo_1' Then
     Go_Block( 'Messages' ) ;
     Execute_query ;
     Go_Block( LC$Bloc ) ;
  End if ;
End ;

Remarque: Lorsque plusieurs timers sont créés, il faut faire appel à la fonction Get_Application_Property( TIMER_NAME ) ; pour récupérer le nom du timer qui vient de se déclencher.

Modification des propriétés d'un timer

A l'exécution, les propriétés d'un timer sont modifiables avec l'instruction:
Set_Timer( 'nom_timer' | id_timer, millisecondes, repetition ) ;

Si vous souhaitez ne modifier qu'une des deux propriétés, utilisez la valeur NO_CHANGE pour conserver la valeur initiale.

Set_Timer()
Sélectionnez

-- Changement de la durée seulement --
Set_Timer( 'Tempo_1', 10000, NO_CHANGE ) ;

-- Changement répétition seulement --
Set_Timer( 'Tempo_1', NO_CHANGE, REPEAT ) ;

L'identifiant interne d'un timer est obtenu avec la fonction Find_Timer( 'nom_timer' ) ;

Destruction d'un timer

Utilisez la fonction Delete_Timer( 'nom_timer' | id_timer ) ;

Attention:
Un timer ne se déclenche que lorsque la forme est en attente de saisie (le focus est dans un item).
Il est inactif lorsque vous exécutez une fonction native, un bloc PL/SQL ou une procédure stockée.
Ne comptez donc pas sur un timer pour quitter prématurément l'exécution d'une fonction native (execute_query) ou une fonction ou procédure sotckée en base ou dans une unité de programme.


Afficher un message dans la barre de statut

Pour afficher un message dans la barre de message, utilisez la fonction Message()

Message( 'message' [, acknowledge | no_acknowledge ] ) ;

acknowledge demande une validation de l'utilisateur.
Dans ce cas, le message n'est pas affiché dans la barre de message mais dans une boite d'alerte.

Remarque:
La barre de statut est affichée dans la fenêtre indiquée dans la propriété Fonctionnel -> Fenêtre de commande du module.
Si cette propriété est positionnée à Null, aucune barre de statut n'est affichée et l'instruction message() n'affichera rien.


Quel code dans quel déclencheur ?

Le nombre de déclencheurs utilisables dans Forms est tellement important que l'on se demande souvent, au début, lequel utiliser.

Rappelez-vous qu'ils ont organisés en « groupes ».
Le groupe PRE-xx rassemble les déclencheurs qui s'exécutent avant la fonction dont il portent le nom
Le groupe POST-xx rassemble les déclencheurs qui s'exécutent après la fonction dont il portent le nom
Le groupe WHEN-xx rassemble les déclencheurs qui s'exécutent pendant la fonction dont il portent le nom
Le groupe ON-xx rassemble les déclencheurs qui remplacent la fonction dont il portent le nom

Les déclencheurs des groupes PRE- et POST- n'acceptent généralement pas les fonctions restreintes car ils s'exécutent souvent pendant la phase de navigation.

Voici quelques conseils sur le choix du déclencheur à surcharger:

· Mettre à jour un item visible ou non visible d'un bloc basé avant l'enregistrement , alimenter une colonne avec un numéro de séquence:
PRE-INSERT, PRE-UPDATE, PRE-DELETE du bloc

· Exécuter un ordre du DML manuellement suite à un enregistrement :
POST-INSERT, POST-UPDATE, POST-DELETE du bloc

· Remplacer les ordres du DML effectués automatiquement par Forms lorsque le bloc est basé :
ON-INSERT, ON-UPDATE, ON-DELETE du bloc

· Faire un contrôle ou initialiser la valeur d'un item, d'un enregistrement ou d'un bloc dès l'arrivée dans l'objet en question :
When-New-Item-Instance, When-New-Record-Instance, When-New-Block-Instance

· Gérer une action spécifique suite à une action clavier de l'utilisateur:
Key-Next-xx, Key-Prev-xx, Key-Up, Key-Down, Key-List-Val, etc.

Attention
Les déclencheurs KEY-xx ne s'exécutent pas lors d'une navigation avec la souris

· Initialiser le comportement de la forme avant que tout objet soit créé en mémoire :
PRE-FORM

· Initialiser le comportement de la forme avant que tout objet soit affiché :
WHEN-NEW-FORM-INSTANCE

· Valider le contenu d'un item ou d'un enregistrement:
WHEN-VALIDATE-ITEM, WHEN-VALIDATE-RECORD


Tester le code retour des functions natives

Les fonctions natives positionnent toujours un code retour indiquant la réussite ou l'échec de l'exécution de la fonction

Ce code retour peut être interrogé via l'une des fonctions suivantes:

  • FORM_SUCCESS
  • FORM_FAILURE
  • FORM_FATAL

Ces fonctions retournent un booléen

Fonction Résultat Valeur retournée
FORM_SUCCESS Ok TRUE
  Ko FALSE
FORM_FAILURE Ok FALSE
  Ko TRUE
FORM_FATAL Ok FALSE
  Ko TRUE

Elles doivent être interrogées immédiatement après l'instruction exécutée.

Utilisez ce code retour pour vous assurer que la suite du code peut s'exécuter.
En effet, la non exécution d'une fonction native n'arrête jamais le traitement

Exemple:

FORM_SUCCESS
Sélectionnez

Begin
   -- Changer de bloc --
   Go_Block( 'EMP' ) ;
   -- Ne pas continuer si fonction Ko --
   If Not Form_Success Then
      Message( 'Impossible d''atteindre le bloc EMP', acknowledge ) ;
      -- Arrêt du traitement --
      Raise Form_Trigger_Failure ;
   Else
      -- On continue… --
      Execute_query ;
   End if ;
Exception
   When Form_Trigger_Failure Then
      -- Propagation de l'exception --
      Raise ;
End ;

Remarque:
FORM_SUCCESS ne doit pas être utilisé après une instruction Commit_Form ou Post.
En effet, l'enregistrement déclenche toute une série d'actions qui positionnent chacune le code retour. Il n'y a donc pas de code retour « général » indiquant que toutes les actions se sont déroulées correctement.

Pour vérifier que la phase d'enregistrement s'est correctement déroulée, testez le statut de la forme

 
Sélectionnez

Commit_Form ;
If :System.Form_Status <> 'QUERY' Then
   Message( 'La validation a échoué', acknowledge ) ;
End if ;



Stopper le déroulement du code

Lorsque vous voulez stopper l'exécution du code PL/SQL dans une procédure ou un déclencheur, utilisez l'exception : Form_Trigger_Failure

Il s'agit d'une exception prédéfinie dans Forms et n'a pas besoin d'être déclarée

Exemple de déclencheur When-Validate-Item:

When-Validate-Item
Sélectionnez

Begin
   If :EMP.Sal > 10000 Then
      Message( 'Le salaire est délirant !', acknowledge ) ;
      -- Arrêt du trigger et retour dans l'item --
      Raise Form_Trigger_Failure ;
   End if ;
End ;



Contrôle d'unicité de la valeur d'un item en temps réel

A un moment ou un autre, lors de la saisie dans un bloc de données se pose le problème du contrôle d'unicité de la valeur saisie.

Il n'est parfois pas acceptable de laisser l'utilisateur saisir une valeur déjà existante qui sera rejetée ultérieurement lors de l'insertion ou de la modification.

La solution généralement implémentée consiste à « poster » sur un déclencheur When-New-Record-Instance et attendre l'éventuel message d'erreur de la base si une clé primaire ou un index unique existe sur cette colonne.

Nous souhaitons pouvoir invalider la saisie dès la validation de l'item.
Cependant, le déclencheur When-Validate-Item n'accepte les procédures restreintes, donc, il n'est pas possible de boucler dans les enregistrements pour exécuter un contrôle d'unicité.

La solution proposée apporte une réponse élégante au problème en utilisant les items calculés.
Cette solution est aimablement fournie par Kevin Clarke (UK)

Dans le bloc de données, un item calculé, numérique, non basé et non affiché récupère le code retour d'une fonction (1=existe déjà, 0=n'existe pas)
Dans un bloc de contrôle, un item calculé, numérique effectue la somme des items calculés du bloc de données.
Si cette somme est supérieure à 0, alors la valeur saisie existe déjà dans le bloc.

Description de la fonction de comparaison:

Afin de pouvoir comparer aussi bien des valeurs de type CHAR que DATE ou NUMBER, nous allons mettre en place, dans une unité de programme ou une bibliothèque PL/SQL un package dans lequel la fonction sera surchargée

 
Sélectionnez

PACKAGE compare IS

  function COMPARAISON (val1 date, val2 date) return number ;
  function COMPARAISON (val1 varchar2, val2 varchar2) return number ;
  function COMPARAISON (val1 number, val2 number) return number ;

END;
 
Sélectionnez

PACKAGE BODY compare IS
  
    -- comparaison dates --
    function COMPARAISON (val1 date, val2 date)
    return number is
    answer number := 0;
    begin 
    if val1 = val2 then answer := 1; end if;
    return(answer);
    end;

    -- comparaison char --
    function COMPARAISON (val1 varchar2, val2 varchar2)
    return number is
    answer number := 0;
    begin 
    if val1 = val2 then answer := 1; end if;
    return(answer);
    end;

    -- comparaison number --
    function COMPARAISON (val1 number, val2 number)
    return number is
    answer number := 0;
    begin 
    if val1 = val2 then answer := 1; end if;
    return(answer);
    end;

END;


Dans notre bloc de données (EMP) nous voulons un contrôle sur l'item ENAME.
La propriété du bloc Enregistrements -> interroger tous les enregistrements doit être valorisée à OUI
Nous ajoutons un item numérique, non basé et non affiché : MATCH_FOUND sur lequel nous définissons les propriétés suivantes:

Général
Type d'élément : Elément texte

Données
Type de données : Number

Calcul
Mode de calcul : Formule
Formule : compare.comparaison(:ctrl.charsave, :emp.ename)

Puis nous créons un bloc de contrôle (non basé) : CTRL
Vous devez valoriser la propriété Enregistrements -> enregistrement unique à OUI
Nous y ajoutons un item non affiché de type CHAR d'une longueur identique à celle de l'item :EMP.ENAME
Enfin un item calculé, non basé et non affiché : MATCH_FOUND avec les propriétés suivantes:

Général
Type d'élément : Elément texte

Données
Type de données : Number

Calcul
Mode de calcul : Récapitulatif
Fonction de récapitulation : Somme
Bloc récapitulatif : EMP
Elément récapitulatif : MATCH_FOUND


Il ne reste plus qu'à implémenter un déclencheur When-Validate-Item sur l'item :EMP.ENAME

Déclencheur When-Validate-Item
Sélectionnez

:CTRL.CHARSAVE := :EMP.ENAME;
If :CTRL.MATCH_FOUND > 1 then
   Message('Ce code existe déjà');
   Raise form_trigger_failure ;
End if;


Cette solution offre les deux avantages suivants:

  • Inutile d'utiliser l'instruction POST pour enregistrer et déclencher l'éventuelle DUPLICATE_KEY au niveau de la base de données
  • Fonctionne sur les colonnes ne disposant pas de clé primaire ou clé unique



Mode Enter Query : modifier la couleur des items interrogeables

Toujours dans l'optique de donner à l'utilisateur le maximum d'informations visuelles, il est intéressant, lors de la saisie des critères en mode interrogation de savoir quels sont les items interrogeables. (ceux qui ont la propriété : Base de données -> Interrogation autorisée)

Un moyen simple à mettre en œuvre est de modifier la couleur de fond des items du bloc qui sont interrogeables.

La librairie PL/SQL TUTO_FORMS.pll contient deux procédures qui réalisent automatiquement cette opération.

La procédure Debut_Query() boucle sur tous les items visibles et interrogeables du bloc en cours et modifie la couleur de fond des items en appliquant l'attribut visuel VA_QUERY (fourni dans la librairie OBJ_TUTO_FORMS.olb).

La procédure Fin_Query() rétablit la couleur d'origine de ces items.

Début_Query est appelée dans un déclencheur : KEY-ENTQRY pour lancer la colorisation et passer en mode interrogation

KEY-ENTQRY
Sélectionnez

Debut_Query ;
Enter_Query ;

Fin_Query() est appelée dans un déclencheur KEY-EXEQRY lorsque l'utilisateur exécute l'interrogation

KEY-EXEQRY
Sélectionnez

Execute_Query ;
If :System.Mode <> 'ENTER-QUERY' Then
    Fin_Query ;
End if ;

Examinez ces fonctions pour comprendre comment boucler sur tous les items d'un bloc à l'exécution.

L'écran de test TEST_COLLECTION.FMB met en œuvre cette fonctionnalité sur le bloc principal (ARTICLES)

Lancer le mode interrogation (F11)
Les items CODE et LIBELLE dont la propriété : interrogation autorisée vaut Oui sont colorés en orange.
Saisissez votre critère dans l'item code (ex. : ART%) et exécuter l'interrogation Ctrl+F11
Les items ont retrouvé leur couleur d'origine


Rappel:
Le déclencheur KEY-ENTQRY s'exécute lorsque l'utilisateur passe en mode interrogation: Menu Interrogation -> Saisir
Icône (enter-query) de la barre d'icônes
Touche de fonction F11

Le déclencheur KEY-EXEQRY s'exécute lorsque l'utilisateur demande l'exécution de l'interrogation:

Menu Interrogation -> Exécuter
Icône (execute-query) de la barre d'icônes
Touche de fonction Ctrl+F11


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2005 SheikYerbouti. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.