Tutoriel Oracle Forms : manipulations dynamiques 2ème partie


précédentsommairesuivant

3. L'écran de gestion des LOV (INIT_LOV.FMB)

Image non disponible

Il présente la liste des modules, blocs et items de votre application

Ces informations sont automatiquement enregistrées en table pour chacun de vos modules grâce à l'appel de procédure Init_referentiel_lov() qui se trouve en commentaire dans chaque trigger WHEN-NEW-FORM-INSTANCE
Pour renseigner l'ensemble de ces informations, il suffit de retirer le commentaire, exécuter le module, quitter et remettre le commentaire

En regard de chaque composant : module, bloc et item, vous pouvez saisir un libellé

Le canvas inférieur contient 3 onglets

Un onglet Items qui affiche la liste des items du bloc et du module sélectionné, un libellé libre ainsi qu'un rappel du format appliqué sur l'item

Image non disponible

Un onglet LOV qui permet de définir les propriétés de la LOV attachée à l'item sélectionné

Nom table permet de définir le nom d'une table qui servira à la recherche globale
Validation permet de définir si la LOV doit servir a valider l'item en saisie
Auto select permet de définir si la LOV affichera la liste de sélection dès son affichage
PosX et PosY, facultatifs, permettent de positionner la LOV
Titre de la LOV permet d'indiquer le titre
Select reçoit la partie Select de la requête (sans le mot clé Select)
From reçoit la partie From de la requête (sans le mot clé From)
Where (facultatif) reçoit la partie Where de la requête (sans le mot clé Where)
Order by (facultatif) reçoit la partie Order by de la requête (sans le mot clé Order by)

La case à cocher Ok non modifiable indique si la LOV est valide et donc accessible à l'utilisateur
Tant que cette LOV n'est pas valide, elle ne sera ni visible ni utilisable pour l'utilisateur

Le bouton Afficher l'ordre select permet de visualiser la requête complète

Image non disponible

Le bouton Contrôle de la syntaxe permet de vérifier la cohérence de la requête, d'alimenter les colonnes de l'onglet Colonnes LOV

Image non disponible

Tant que vous n'avez pas la certitude que votre LOV s'exécutera correctement, il est inutile de répondre Oui pour la reconstruction des correspondances de colonne
(Cette opération remplace les définitions de colonnes de cette LOV dans la table LOV_ELEMENT_COLONNE et par conséquent les réglages utilisateurs éventuellement sauvegardés)


Et d'afficher dans une LOV native le résultat de la requête

Image non disponible

Nous n'avons utilisé pour cela qu'un unique groupe d'enregistrement et une unique LOV native !

Pourtant nous pouvons représenter n'importe quelle LOV de 2 à 9 colonnes…

Dans cet exemple, nos 3 colonnes sont correctement affichées, dimensionnées et titrées

Image non disponible

L'onglet Colonnes LOV permet de configurer chaque colonne de la LOV

Ordre permet d'ordonner l'affichage par défaut des colonnes
Colonne contient le nom de la colonne
Item de réception reçoit l'item qui devra recevoir la valeur de cette colonne
Rech permet de définir la colonne de recherche par défaut
Val permet de définir la colonne qui servira à valider l'item
(Les LOV natives de Forms ne permettent d'utiliser que la première colonne pour la validation des items. Ici, vous pouvez sélectionner n'importe laquelle)
Taille permet de définir la largeur d'affichage en caractères
Libellé colonne LOV permet de définir le libellé tel qu'il apparaîtra dans la LOV


Image non disponible

Clause permet de définir une clause restrictive sur la colonne. Cette clause peut contenir une référence à un autre item de la forme (Placeholder). Dans ce cas, il doit se conformer à la syntaxe Forms :bloc.item (par exemple >2 ou bien = :bloc.item, ou n'importe quel ordre sql pouvant apparaître dans la clause Where)

Valeur de test permet de saisir la valeur de remplacement du placeholder (uniquement pour la validation de la LOV)



Rentrons dans le détail

La procédure Init_referentiel_lov(),présente dans le trigger WHEN-NEW-FORM-INSTANCE permet d'initialiser pour chaque forme les items qui la compose (cette procédure se trouve dans la librairie Forms LOV.PLL)

Le nom du module (Name_in( 'system.current_form' )) est enregistré dans la table LOV_MODULE Begin Insert into LOV_MODULE ( NOM_MODULE ,LIBELLE ) Values ( LC$Module ,Null ); Exception When DUP_VAL_ON_INDEX then Null ; End; Le traitement de l'exception DUP_VAL_ON_INDEX permet de faire l'économie d'un Select pour vérifier si l'item existe déjà.

On sélectionne le premier bloc de la forme avec l'instruction : LC$Block := Get_Form_Property( LC$Module, FIRST_BLOCK ) ; Et l'on boucle sur tous les blocs While LC$Block is not null Loop -- pour chaque block -- Le nom du bloc est enregistré dans la table LOV_BLOCK Begin Insert into LOV_BLOCK ( MOD_NOM_MODULE ,NOM_BLOCK ) Values ( LC$Module ,LC$Block ); Exception When DUP_VAL_ON_INDEX then Null ; End; On se positionne sur le premier item du bloc avec l'instruction suivante : LC$Item := Get_Block_Property( LC$BLOCK, FIRST_ITEM ) ; Puis l'on boucle sur tous les items de ce bloc While LC$Item is not null Loop -- Pour chaque item On rejette les items de type BUTTON ou de type LONG If Get_Item_Property( LC$Bitem, ITEM_TYPE ) <> 'BUTTON' Then LC$Type := Get_Item_Property( LC$Bitem, DATATYPE ) ; If LC$Type <> 'LONG' Then Le libellé de l'item est issu de son prompt LC$Prompt := Substr( Get_Item_Property( LC$Bitem, PROMPT_TEXT ), 1, 80 ) ; Le format est également sauvegardé LC$Format := Get_Item_Property( LC$Bitem, FORMAT_MASK ) ; Il servira à mettre en conformité de format la valeur de retour de la LOV

Et l'on enregistre chaque item dans la table LOV_ITEM Begin Insert into LOV_ITEM ( MODULE ,BLOC ,ITEM ,LIBELLE ,ID ,ITEM_TYPE ,FORMAT ) Values ( LC$Module ,LC$Block ,LC$Item ,LC$Prompt ,SEQ_ITEM.NEXTVAL ,Decode( LC$Type, 'CHAR', 'C', 'DATE', 'D', 'N' ) ,LC$Format ); Exception When DUP_VAL_ON_INDEX then Null ; End;

Un seul groupe d'enregistrement et une seule LOV native pour simuler n'importe quelle LOV de 2 à 9 colonnes ?
Comment cela fonctionne t-il ?

Pour cela nous allons utiliser un groupe d'enregistrement construit dynamiquement

Si l'on peut construire dynamiquement un groupe d'enregistrement, il n'en va pas de même pour une LOV native
Celle-ci doit exister physiquement dans la forme
Puisque nous nous autorisons à gérer des LOV de 2 à 9 colonnes, il faut donc disposer d'une LOV native composée de 9 colonnes à laquelle nous associerons ensuite le groupe d'enregistrements, qui sera lui-même composé de 9 colonnes
Une LOV native LV_SYNTAXE est donc créée dans la forme composée de 9 colonnes COL1, COL2,...,COL9
Si la requête fait référence à moins de 9 colonnes, des colonnes NULL seront ajoutées au groupe d'enregistrement pour coïncider avec la LOV native de contrôle d'une part, et la procédure stockée qui renseigne également 9 colonnes d'autre part


Ouvrons la procédure Check_sql_order() du module INIT_LOV.FMB

Définissons une variable de type record_group rg_name VARCHAR2(40) := 'RG_SYNTAXE2'; rg_id RecordGroup; Reconstituons l'ordre SQL complet et jetons le en pâture au paquetage Oracle DBMS_SQL ------------------------------------------ -- Verifie que l'ordre SQL est conforme -- ------------------------------------------ -- LC$Req := 'Select ' || CHR(10) || :BL_LOV.ORDRE || CHR(10) || ' From ' || CHR(10) || :BL_LOV.FROM ; If :BL_LOV.FILTRE is not null Then LC$Req := LC$Req || CHR(10) || ' Where ' || CHR(10) || :BL_LOV.FILTRE ; End if ; If :BL_LOV.T_ORDERBY is not null Then LC$Req := LC$Req || CHR(10) || ' Order BY ' || CHR(10) || :BL_LOV.T_ORDERBY ; End if ; LC$Req := Rtrim( LC$Req, ';' ) ; Begin c := dbms_sql.open_cursor; dbms_sql.parse(c,LC$Req, 1); d := dbms_sql.execute(c); Exception When others Then dbms_sql.close_cursor(c); :BL_LOV.OK := 'N' ; Set_Alert_Property( 'AL_ERREUR', TITLE, 'Contrôle de l''ordre SQL' ) ; Set_Alert_Property( 'AL_ERREUR', ALERT_MESSAGE_TEXT, 'Ordre SQL incorrect' ) ; LN$Cpt := Show_Alert( 'AL_ERREUR' ) ; Raise form_trigger_failure ; End ; dbms_sql.describe_columns2(c, col_cnt, rec_tab); dbms_sql.close_cursor(c); Si la requête ne franchit pas cette étape, c'est qu'elle est incorrecte

Nous rejetons également les colonnes de type LONG, LONGRAW, RAW et les LOBs LB$Ret := TRUE ; For i IN rec_tab.first .. rec_tab.last Loop If rec_tab(i).col_type IN (8,23,24,113,114) Then Set_Alert_Property( 'AL_ERREUR', TITLE, 'Contrôle de l''ordre SQL' ) ; Set_Alert_Property( 'AL_ERREUR', ALERT_MESSAGE_TEXT, rec_tab(i).col_name || ' Colonnes LONG, LONGRAW, RAW, BLOB, BFILE non autorisées' ) ; LN$Cpt := Show_Alert( 'AL_ERREUR' ) ; LB$Ret := FALSE ; End if ; End loop ; If not LB$Ret Then Raise form_trigger_failure ; End if ; Nous n'acceptons pas non plus les LOV de moins de 2 colonnes ou de plus de 9 colonnes If col_cnt < 2 or col_cnt > 9 Then Set_Alert_Property( 'AL_ERREUR', TITLE, 'Contrôle de l''ordre SQL' ) ; Set_Alert_Property( 'AL_ERREUR', ALERT_MESSAGE_TEXT, 'l''ordre SQL doit contenir entre 2 et 9 colonnes' ) ; LN$Cpt := Show_Alert( 'AL_ERREUR' ) ; :BL_LOV.OK := 'N' ; Raise form_trigger_failure ; End if ; Nous ajoutons ensuite à la clause where les clauses éventuellement définies au niveau de chaque colonne de l'onglet Colonnes LOV

L'extraction des colonnes de la partie Select de l'ordre, ainsi que l'ajout éventuel des colonnes NULL manquantes sont réalisés par la procédure Build_Select_line()

Puis nous construisons notre groupe d'enregistrements dynamique à l'aide de la requête stockée dans la variable LC$Req rg_id := Find_Group( rg_name ); IF Not Id_Null(rg_id) THEN Delete_group( rg_id ) ; End if; rg_id := Create_Group_From_Query( rg_name, LC$Req ) ; errcode := Populate_Group( rg_id ); Si cette étape est encore franchie et que nous avons répondu Oui à la demande de reconstruction des colonnes de la LOV, l'onglet Colonnes LOV est pré alimenté

Enfin nous associons à notre LOV native le groupe d'enregistrement fraîchement créé Set_Lov_Property( 'LV_SYNTAXE', GROUP_NAME, rg_name ) ; Nous réglons les titre et largeur par défaut des colonnes -- Mise a jour titre et largeur des colonnes -- For i IN rec_tab.first..rec_tab.last Loop -- Titre -- Set_Lov_Column_Property( 'LV_SYNTAXE', i, TITLE, rec_tab(i).col_name ) ; -- Largeur -- LN$Pos := 30 ; If rec_tab(i).col_type IN (1,96) Then If rec_tab(i).col_max_len > 30 Then LN$Pos := 30 ; Else LN$Pos := rec_tab(i).col_max_len ; End if ; Elsif rec_tab(i).col_type = 2 Then If rec_tab(i).col_precision = 0 Then LN$Pos := 10 ; Else LN$Pos := rec_tab(i).col_precision ; End if ; Elsif rec_tab(i).col_type = 12 Then LN$Pos := 12 ; Elsif rec_tab(i).col_type IN (112,113) Then LN$Pos := 30 ; End if ; LN$Pos := LN$Pos * 6 ; Set_Lov_Column_Property( 'LV_SYNTAXE', i, WIDTH, LN$Pos ) ; End loop ; Masquons les colonnes inutiles (NULL) en positionnant leur largeur à 0 -- Masquage des colonnes non renseignées -- If rec_tab.last < 9 Then For i IN rec_tab.last + 1..9 Loop Set_Lov_Column_Property( 'LV_SYNTAXE', i, WIDTH, 0 ) ; End loop ; End if ; Et affichons la LOV native LB$Ret := Show_Lov('LV_SYNTAXE') ;


Gestion des LOV



Nous avons défini une LOV sur notre item et vérifié que sa syntaxe est correcte

Que se passe t-il lorsque l'utilisateur utilise son application ?

Lorsqu'une LOV native est présente sur un item de saisie, la barre d'état affiche l'indication "Liste de valeur…"
Lorsque cette LOV native est surchargée par une LOV générique, elle est automatiquement désactivée car devenue inutile.
Dans ce cas la barre d'état n'indique plus à l'utilisateur la présence d'une LOV disponible !
C'est pourquoi, au lancement de chaque forme, la procédure Init_item_lov() stockée dans la librairie LOV.PLL, est appelée depuis le trigger WHEN-NEW-FORM-INSTANCE


Cette procédure recherche pour l'écran en cours la liste des LOV génériques que vous avez défini, inhibe les éventuelles LOV natives concurrentes en fixant la propriété LOV_NAME à NULL -- Désactivation de l'éventuelle LOV native -- Set_Item_Property( LC$Item, LOV_NAME, '' ) ; Set_Item_Property( LC$Item, VALIDATE_FROM_LIST, PROPERTY_FALSE ) ; Et indique à l'utilisateur la présence de la LOV en ajoutant (LOV) à la fin du Hint…
C'est la raison pour laquelle il est impératif que la propriété Aide automatique de vos items soit positionnée à OUI !

Lorsqu'il s'agit d'une LOV de validation, l'indicateur (LOVV) est ajouté

L'utilisateur entre dans le champ de saisie et demande l'affichage de la LOV avec les touches Ctrl+L

Le trigger de niveau forme KEY-LISTVAL se déclenche et appelle la procédure Display_lov() stockée dans la librairie LOV.PLL

Celle-ci commence par vérifier qu'une LOV générique à été définie pour cet item
Si elle n'en trouve pas, elle vérifie qu'une LOV native existe et l'exécute. (ce système permet de conserver le fonctionnement des LOV natives que vous n'aurez pas jugé bon de surcharger)

Si elle trouve une LOV générique, elle récupère dans les tables les informations nécessaires, reconstruit la clause where à partir des clauses de colonne éventuellement définies dans l'onglet Colonnes LOV et appelle l'écran de LOV générique GEN_LOV via la procédure Call_gen_lov() ------------------------------ -- Appel de l'écran GEN_LOV -- ------------------------------ PROCEDURE CALL_GEN_LOV ( PN$Item IN NUMBER, PC$Table IN VARCHAR2, PC$Select IN VARCHAR2, PC$From IN VARCHAR2, PC$Where IN VARCHAR2, PC$Orderby IN VARCHAR2, PC$Affiche IN VARCHAR2, PN$PosX IN NUMBER, PN$PosY IN NUMBER, PC$Titre IN VARCHAR2 ) IS pl_id ParamList; pl_name VARCHAR2(10) := 'gen_lov'; BEGIN -- Création de la liste de paramètres -- pl_id := Get_Parameter_List(pl_name); IF NOT Id_Null(pl_id) THEN Destroy_Parameter_List(pl_id); End if ; pl_id := Create_Parameter_List(pl_name); IF Id_Null(pl_id) THEN Message('Erreur de création de liste de paramètres '||pl_name); RAISE Form_Trigger_Failure; END IF; -- Ajout des paramètres à la liste -- Add_Parameter(pl_id,'UTI_ID' ,TEXT_PARAMETER, Name_in('PARAMETER.UTI_ID')); Add_Parameter(pl_id,'ITEM_ID' ,TEXT_PARAMETER, PN$Item); Add_Parameter(pl_id,'PM$TABLE' ,TEXT_PARAMETER, PC$Table); Add_Parameter(pl_id,'P_SELECT' ,TEXT_PARAMETER, PC$Select); Add_Parameter(pl_id,'P_FROM' ,TEXT_PARAMETER, PC$From); Add_Parameter(pl_id,'P_WHERE' ,TEXT_PARAMETER, PC$Where); Add_Parameter(pl_id,'P_ORDERBY' ,TEXT_PARAMETER, PC$Orderby); Add_Parameter(pl_id,'TITRE' ,TEXT_PARAMETER, PC$Titre); Add_Parameter(pl_id,'AUTO_SELECT',TEXT_PARAMETER, PC$Affiche); Add_Parameter(pl_id,'POSX' ,TEXT_PARAMETER, PN$PosX); Add_Parameter(pl_id,'POSY' ,TEXT_PARAMETER, PN$PosY); -- Appel de l'écran LOV générique -- CALL_FORM('GEN_LOV', NO_HIDE, DO_REPLACE, QUERY_ONLY, pl_id); END;GEN_LOV est le nom de la forme appelée
NO_HIDE indique de ne pas masquer la forme appelante
DO_REPLACE remplace le menu de la forme appelante
QUERY_ONLY place l'écran appelée en mode non modifiable
pl_id est le nom de la liste des paramètres

Au retour, elle place les valeurs sélectionnées dans les différents items définis également dans l'onglet Colonne LOV ------------------------------ -- Récupération des valeurs -- ------------------------------ T_VALEURS := PKG_GESTION_LOV.GET_Valeurs_LOV ; -- Valorisation des items de retour -- If Tab_col.first is not null Then For i IN Tab_col.first..Tab_col.last Loop If Tab_col(i).bloc_item_retour is not null Then Case i When 1 Then LC$Valeur := T_VALEURS.col1 ; When 2 Then LC$Valeur := T_VALEURS.col2 ; When 3 Then LC$Valeur := T_VALEURS.col3 ; When 4 Then LC$Valeur := T_VALEURS.col4 ; When 5 Then LC$Valeur := T_VALEURS.col5 ; When 6 Then LC$Valeur := T_VALEURS.col6 ; When 7 Then LC$Valeur := T_VALEURS.col7 ; When 8 Then LC$Valeur := T_VALEURS.col8 ; When 9 Then LC$Valeur := T_VALEURS.col9 ; End case ; If LC$Valeur is not Null Then -- Copie de la valeur dans l'item -- Copy( LC$Valeur, Tab_col(i).bloc_item_retour ) ; End if ; End if ; End loop ; End if ;


précédentsommairesuivant

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

Cet article s'applique à la version 9i et 10g d'Oracle Forms