Sztuczki, triki i nie tylko..

Absolwent Wydziału Elektrycznego Politechniki Poznańskiej. Od 1993 roku jest pracownikiem Instytutu Informatyki Politechniki Poznańskiej. Zainteresowania naukowe obejmują m.in. optymalizację zapytań w systemach baz danych, problemy modelowania danych, inżynierię oprogramowania i narzędzia CASE. Doświadczenia zawodowe obejmują m.in. projektowanie i budowę dużych systemów informatycznych (kierownik projektu rozproszonego systemu informatycznego dla wyższej uczelni – Dziekanat96) i administrowanie RDBMS Oracle. Od lipca 1997 roku opiekuje się działem dla twórców aplikacji w Oracle’owych PLOUG’tkach. Autor zachęca wszystkich czytelników do zgłaszania swoich problemów napotykanych podczas budowy aplikacji lub choćby do przesyłania uwag dotyczących zawartości działu, który w dużej mierze powinien zależeć od czytelników.

Maciej Matysiak
matys@cs.put.poznan.pl


Sztuczki, triki i nie tylko…

Dynamiczne listy typu combo box w Oracle Forms 4.5

Statyczne listy wartości typu Combo Box (lub Poplit i T-list) w Oracle Forms 4.5 nie są takie statyczne na jakie wyglądają. Kilka drobnych zabiegów pozwala je „ożywić”.
Listy wartości to elementy interfejsu użytkownika, które znakomicie przyspieszają wybór jednego z wielu elementów znajdujących się w słownikach (katalogach), uwalniając użytkownika od konieczności wpisywania z klawiatury długich nazw (np. nazw produktów, województw, kategorii, itp.) lub pamiętania dziesiątek lub setek numerycznych identyfikatorów. Listy wartości (LOV – List Of Values znane są użytkownikom Oracle już od wersji 2.5 SQL*Forms. W każdej kolejnej wersji Forms-ów Oracle oferuje nowe własności list wartości, umożliwiając wykonywanie coraz bardziej przyjaznych dla użytkownika interfejsów. Jednak nawet w wersji 4.5 Oracle Forms, podczas definiowania list wartości, twórcy aplikacji napotykają ograniczenia wynikające nie z „wyobraźni”, ale z samego narzędzia.
Oracle Forms 4.5 oferuje dwa rodzaje list wartości: statyczną listę elementów (Combo Box, Poplist lub T-list) i dynamiczną listę wartości pojawiającą się jako nowe okienko (LOV). Pierwsza z nich jest znacznie wygodniejsza w użyciu w przypadku niedużej liczby pojedynczych wartości, natomiast druga jest wygodniejsza w przypadku dużej liczby wartości lub w przypadku złożonej struktury rekordów. Problem pojawia się wtedy, gdy mamy zaoferować użytkownikowi wybór jednej z niewielu pojedynczych wartości, ale lista tych elementów nie jest stała i może ulegać zmianie. Możliwe są trzy następujące rozwiązania. Po pierwsze, aktualny zbiór wartości możemy „zaszyć” w definicji statycznej listy. Zaletą rozwiązania jest wygoda wyboru dla użytkownika, ale wadą jest konieczność modyfikowania aplikacji (lub nawet kilku aplikacji) w przypadku potrzeby dodania choćby jednego elementu do listy. Po drugie, aktualny zbiór wartości możemy przechowywać w tabeli w bazie danych i zdefiniować w aplikacji dynamiczną listę wartości typu LOV. Zaletą rozwiązania jest to, że listę można modyfikować na poziomie bazy danych bez konieczności modyfikowania aplikacji, ale do wyboru jednego elementu z kilku lub kilkunastu tego typu lista wydaje się znacznie mniej wygodna w porównaniu do poprzedniego rozwiązania – z listą typu Combo Box. Można też zastosować trzecie rozwiązanie, które polega na obejściu problemu „statyczności” list statycznych, tak aby ich zawartość zmieniała się odpowiednio do aktualnej zawartości słownika (katalogu) w bazie danych. Porównajmy te trzy rozwiązania na przykładzie prościutkiej bazy danych z pracownikami.
Załóżmy, że chcemy przechowywać podstawowe dane o pracownikach firmy. Wszyscy pracownicy zostali podzieleni na kilka zespołów, tak, że istnieje potrzeba pamiętania i wyświetlania nazwy zespołu dla każdego pracownika. W przypadku pierwszego rozwiązania – listy statycznej (Rys.1) – pole Zespół zdefiniujemy jako List Item a nazwy zespołów wpiszemy jako własność List Elements. Niestety, w przypadku, gdy zostanie utworzony nowy zespół, np. „ADMINISTRACJA”, niezbędne będzie zmodyfikowanie aplikacji. Takie rozwiązanie jest raczej nie do zaakceptowania.

Statyczna lista wartości typu

Rys.1. Statyczna lista wartości typu „Combo Box” – wygoda użytkowania

Drugim rozwiązaniem jest przechowywanie nazw wszystkich zespołów jako tzw. słownika (katalogu) w dodatkowej tabeli w bazie danych i zdefiniowanie w aplikacji dynamicznej listy wartości typu LOV (Rys. 2). Pole Zespół zdefiniujemy w tym przypadku jako Text Item, którego własność LOV będzie wskazywała na listę wartości zdefiniowaną m.in. za pomocą zapytania w języku SQL, które odwołuje się do tablicy słownikowej. Dynamiczna natura takiej listy powoduje, że po dopisaniu do słownika nowego zespołu, np. „ADMINISTRACJA”, pojawia się on automatycznie na liście jako nowy element wyboru. Jednak do wyboru na liście jednego elementu z pięciu, jak w tym przykładzie, dodatkowe okienko LOV zawierające trzy przyciski, suwaki poziomy i pionowy oraz pole Znajdź (lub Find) do przeszukiwania listy, jest konstrukcją zdecydowanie zbyt skomplikowaną i niezbyt wygodną w użyciu.

Dynamiczna lista wartości typu LOV - dynamiczny słownik

Rys. 2. Dynamiczna lista wartości typu LOV – dynamiczny słownik

Rozwiązaniem optymalnym wydaje się w tym przypadku dynamiczna lista wartości typu Combo Box. Oracle Forms 4.5 nie oferuje w sposób bezpośredni możliwości zdefiniowania takiej listy, ale dla wymagających twórców aplikacji (na potrzeby wymagających użytkowników) zostawiona została furtka w postaci wbudowanych procedur, z których najczęściej używane to Add_List_Element i Populate_List. Można je wykorzystać do programowego przetwarzania list statycznych, tak aby zachowywały się jak listy dynamiczne. Rysunek 3 przedstawia listę typu Combo Box, na której pojawiają się aktualne wartości odczytane z bazy danych, tak jak w przypadku list typu LOV. Rozwiązanie to łączy w sobie wygodę obsługi dla użytkownika i dynamikę listy, dzięki której unikniemy niebezpieczeństwa częstego modyfikowania aplikacji. Można sobie jednak na nie pozwolić jeśli wyświetlane wartości nie przekraczają 30 znaków i są to pojedyncze wartości, a nie rekordy.

Dynamiczna lista typu Combo Box - wygoda użytkowania + dynamiczny słownik

Rys. 3. Dynamiczna lista typu Combo Box – wygoda użytkowania + dynamiczny słownik

Różnych rozwiązań takiej listy daje się opracować zapewne sporo. Poniżej chciałbym przedstawić dwa alternatywne rozwiązania, które wydają mi się najprostsze. Pierwsze wykorzystuje procedurę Add_List_Element, a drugie procedurę Populate_List.
Idea pierwszego rozwiązania – z procedurą Add_List_Element – jest następująca:

  1. Definiujemy pole typu List Item wyświetlane jako Combo Box, Poplist lub Tlist (wg preferencji użytkownika) pozostawiając pustą listę elementów List Elements.
  2. Definiujemy wyzwalacz PRE-FORM, którego zadaniem jest odczytanie elementów listy z bazy danych i dopisanie ich do List Elements. W przykładzie z rysunku 3, wyzwalacz wygląda następująco:

DECLARE
 CURSOR cur_zespoly IS
    SELECT nazwa
    FROM zespoly
    ORDER BY 1 desc;
 var_nazwa zespoly.nazwa%TYPE;
BEGIN
 OPEN cur_zespoly;
 LOOP
  FETCH cur_zespoly INTO var_nazwa;
 EXIT WHEN cur_zespoly%NOTFOUND;
 Add_List_Element('ZESPOL', 1, var_nazwa, var_nazwa);
 END LOOP;
END;

W zasadzie, elementy listy powinny być ponumerowane, do czego służy drugi argument procedury Add_List_Element, ale nadanie wszystkim elementom listy tego samego indeksu znacznie upraszcza powyższy program. Trzeba mieć tylko świadomość tego, że każdy nowy element wstawiany jest na początek listy, więc zapytanie definiujące kursor powinno zwrócić rekordy w porządku malejącym, aby na liście uzyskać porządek rosnący.
Idea drugiego rozwiązania – z procedurą Populate_List – jest następująca:

  1. Definiujemy pole typu List Item – tak jak w poprzednim przypadku.
  2. Definiujemy grupę rekordów (Record Group) zawierające zapytanie SQL (w przykładzie z rysunku 3: SELECT nazwa, nazwa FROM zespoly ORDER BY 1)
  3. Definiujemy wyzwalacz PRE-FORM, którego zadaniem jest wypełnienie wartościami zdefiniowanej grupy rekordów i na jej podstawie zbudowanie listy elementów. W przykładzie z rysunku 3, wyzwalacz wygląda następująco:



DECLARE
 var_err NUMBER;
BEGIN
 var_err := Populate_Group_With_Query('RG_ZESPOLY','SELECT nazwa,
            nazwa FROM zespoly ORDER BY 1');
 Populate_List('ZESPOL','RG_ZESPOLY');
END;

Krok 2 można pominąć jeśli grupę rekordów utworzymy
programowo w wyzwalaczu, ale wówczas komplikuje się nieco treść programu. Ponadto, nie można wówczas współdzielić tej samej grupy rekordów pomiędzy kilka list na tym samym formularzu.
Rozwiązanie pierwsze jest prostsze i wygodniejsze jeśli formularz ma zawierać tylko jedną lub dwie dynamiczne listy typu Combo Box, ponieważ wymaga zdefiniowania mniejszej liczby elementów formularza. W przypadku gdy formularz ma posiadać wiele tego typu list, prostsze wydaje się drugie rozwiązanie, ponieważ grupa rekordów może być współdzielona pomiędzy kilka list.
Oprócz procedur Add_List_Element i Populate_List, do przetwarzania elementów typu List Item można wykorzystać również sześć innych procedur: Convert_Other_Value, Delete_List_Element, Get_List_Element_Count, Get_List_Element_Lable, Get_List_Element_Value oraz Retrieve_List.

Maciej Matysiak
matys@cs.put.poznan.pl