Pretius: strategiczna fuzja jako odpowiedź na współczesne wyzwania
Pretius. Budujemy mądrzej:
strategiczna fuzja jako odpowiedź na współczesne wyzwania

Przewodnik krok po kroku po interaktywnych tabelach z dynamicznymi typami kolumn/danymi wejściowymi

Tomáš Kucharzyk

Oracle APEX Developer

  • 9 października, 2025

Spis

[Uwaga] Ten artykuł został pierwotnie przygotowany w języku angielskim i został przetłumaczony na język polski.

Czasami docieramy do granic natywnych komponentów Oracle Apex i musimy improwizować oraz wdrażać workarounds, aby osiągnąć potrzebną funkcjonalność, taką jak wiele typów inputów w jednej kolumnie interactive grid. W tym artykule chciałbym zaprezentować mój sposób na zrobienie tego bez zbędnego hackowania, zachowując wszystkie natywne funkcjonalności.

Jak osiągnąć dynamiczne inputy

Może to brzmieć skomplikowanie, ale przechodząc przez to rozwiązanie, zobaczymy, że jest proste. Wyjaśnijmy je na przykładzie dwóch use cases. Najpierw zaczniemy od czegoś bardzo łatwego, a następnie dodamy pewne utrudnienia.

  1. Para Label-Value
  2. Dynamiczny typ kolumny z utrudnieniami

Para Label-Value

W przypadku pary Label-Value funkcjonalność jest prosta. Chcemy mieć dwie kolumny, gdzie po lewej stronie mamy label (etykietę), a po prawej value (wartość).

Możemy mieć wiele wierszy, a każdy input wartości może być innego typu. Zobaczmy, jak możemy to osiągnąć w interactive grid.

Konfiguracja tabeli i aplikacji

Tworzymy tabelę DYNAMIC_IG_INPUTS, która zawiera 5 kolumn: Label, typ kolumny, a następnie właściwe kolumny wartości dla varchar, date i number. Moglibyśmy również użyć tylko jednej kolumny varchar dla wartości i zastosować case, w którym rozdzielamy wartości z bazy danych na wiele kolumn w interactive grid i z powrotem, ale na razie postawimy na prostotę.

Column Name Data Type
ID NUMBER
LABEL VARCHAR2(100 CHAR)
COLUMN_TYPE VARCHAR2(100 CHAR)
VALUE_VARCHAR VARCHAR2(100 CHAR)
VALUE_NUMBER NUMBER
VALUE_DATE DATE

Do tabeli dodamy trzy wiersze: atrybut Text z typem varchar, atrybut Number z typem number oraz atrybut Date z typem date.

W aplikacji Apex tworzymy włączony interactive grid. Proces DML będzie automatyczny: używamy ID jako primary key i dodajemy Static ID mygrid do atrybutów grida. Wynik będzie wyglądał jak poniżej.

Ekran pokazujący ustawienia APEX.

Dodawanie pożądanej funkcjonalności

Aby pokazać/ukryć odpowiednią kolumnę w zależności od typu, utworzymy prostą funkcję JavaScript.

/**
 * Sets column visibility in an Oracle APEX Interactive Grid based on the COLUMN_TYPE value for each row.
 * 
 * @param {string} pi_grid - Static ID of the Interactive Grid region
 * 
 * This function dynamically shows or hides cells in each row based on the value stored in the "COLUMN_TYPE" column.
 * For each row, it shows only cells with a CSS class matching the COLUMN_TYPE value ('dropdown', 'date', 'varchar', 'number'),
 * plus any cells with the 'donothide' or 'has-button' class. All other cells are hidden.
 * 
 * The function optimizes performance by:
 * - Using a reusable filter function to avoid repetitive code
 * - Processing all records in a single model traversal
 * - Skipping invalid column types
 */
function setcolumns(pi_grid) {
  const region = apex.region(pi_grid);
  const grid = region.widget().interactiveGrid("getViews", "grid");
  const model = grid.model;
  const gridElement = region.element;

  // Create a reusable filterAndShow function outside the loop
  const filterAndShow = (row$, columnType) => {
    // If there is a - in the columnType, take only the part after it
    if (columnType.includes('-')) {
      columnType = columnType.split('-')[1];
    }
    const td = row$.find("td");
    // Hide all cells that don't match the column type and don't have the donothide class
    td.each(function() {
      const cell = $(this);
      const shouldShow = cell.hasClass(columnType) || cell.hasClass('donothide') || cell.hasClass('has-button');
      cell.toggle(shouldShow);
    });
  };

  // Define valid column types to avoid repetitive if/else statements
  const validTypes = ['dropdown', 'date', 'varchar', 'number'];

  // Process all records in one go
  model.forEach(function(record) {
    const rowId = model.getRecordId(record);
    var columnType = model.getValue(record, "COLUMN_TYPE");
    console.log(columnType);
    console.log(`Row ID: ${rowId}, Column Type: ${columnType}`);
    // If the column type is object take a v value
    if (typeof columnType === 'object' && columnType !== null) {
      columnType = columnType.v;
    }
    // If columnType is not valid, default to varchar
    if (!columnType || !validTypes.some(type => columnType.includes(type))) {

      columnType = 'varchar';

      console.log(`Row ID: ${rowId} has invalid column type, defaulting to varchar`);
    }
    const row$ = gridElement.find(`tr[data-id='${rowId}']`);
    filterAndShow(row$, columnType);
  });
}

Aby precyzyjnie celować w kolumny i komórki, dodamy kilka klas do atrybutów kolumn. Do kolumny VALUE_VARCHAR dodajemy varchar. To samo robimy dla pozostałych kolumn: date dla VALUE_DATE i number dla kolumny VALUE_NUMBER.

Ekran pokazujący ustawienia APEX.

Dla kolumn Label i Column type dodajemy klasę donothide. Zapobiegnie to ukrywaniu tych kolumn.

Ekran pokazujący ustawienia APEX.

Teraz wszystko jest gotowe, aby zobaczyć wynik. Jeśli uruchomimy funkcję w konsoli (setcolumns(’mygrid’);), zobaczymy, że niektóre kolumny zniknęły. Jeśli spróbujemy edytować komórki widoczne w kolumnie Value Varchar, zobaczymy, że komórki są różnego typu.

Ekran pokazujący wynik.

Porządkowanie i CSS

Wygląda na to, że rozwiązanie działa, ale nie prezentuje się najlepiej. Najpierw zmienimy nazwę kolumny Value Varchar na Value.

Ekran pokazujący ustawienia APEX.

Dodamy Dynamic Action, która automatycznie uruchomi dla nas funkcję JavaScript. Dodałem akcję dla zdarzenia Row Initialization [Interactive Grid], ale możesz dodać wiele zdarzeń, aby wyzwalać funkcję w większej liczbie przypadków.

Ekran pokazujący ustawienia APEX.

W sekcji True actions dodajemy setcolumns(’mygrid’);.

Ekran pokazujący ustawienia APEX.

Następnie dodamy podstawowy kod CSS, aby ukryć pozostałe kolumny. Musimy ukryć dwie ostatnie kolumny wraz z nagłówkiem i komórkami. Można to zrobić w bardziej wyrafinowany sposób, ale poniższy kod zadziała w sam raz.

/*Hiding COL*/
#mygrid .a-GV-table col:nth-last-child(1),
#mygrid .a-GV-table col:nth-last-child(2),
/*Hiding TH*/
#mygrid .a-GV-table th:nth-last-child(1),
#mygrid .a-GV-table th:nth-last-child(2),
/*Hiding TD*/
#mygrid .a-GV-table td:nth-last-child(1),
#mygrid .a-GV-table td:nth-last-child(2){
    display: none;
}

Teraz ukończony interactive grid wygląda następująco. Wszystkie walidacje itp. będą działać, ponieważ w rzeczywistości istnieje wiele kolumn. Możemy również ustawić walidację required w oparciu o wartość typu kolumny.

Ekran pokazujący wynik.

Dynamiczny typ kolumny z utrudnieniami

W drugim przypadku dodamy kilka utrudnień:

  • Chcemy dodawać wiersze
  • Chcemy wybierać, jakiego typu kolumny chcemy użyć dla dodanego wiersza
  • I chcemy używać dynamicznego menu rozwijanego (wiele typów list wyboru – różne dropdowny dla różnych typów)

Konfiguracja tabeli i aplikacji

Najpierw tworzymy kolejną tabelę DYNAMIC_IG_INPUTS2, która zawiera pięć kolumn: Label, column type oraz kolumny value dla varchar, dropdown (liczba) i date.

Column Name Data Type
ID NUMBER
LABEL VARCHAR2(100 CHAR)
COLUMN_TYPE VARCHAR2(100 CHAR)
VALUE_VARCHAR VARCHAR2(100 CHAR)
VALUE_DROPDOWN VARCHAR2(100 CHAR)
VALUE_DATE DATE

Kolejny krok jest taki sam jak wcześniej: utworzenie nowego, włączonego interactive grid ze Static ID mygrid z automatycznym procesem.

Utworzymy jeszcze jedną tabelę: DYNAMIC_LOV. Posłuży ona jako typ kolumny dla grida. Będzie miała dwie kolumny: display_value i return_value.

Column Name Data Type
DISPLAY_VALUE VARCHAR2(100 CHAR)
RETURN_VALUE VARCHAR2(100 CHAR)

Dodamy kilka wartości do tabeli.

Ekran pokazujący wartości.

I ustawimy kolumnę Column Type w gridzie jako Select list z wartościami z dynamic_lov.

Ekran pokazujący ustawienia APEX.

Ustawimy kolumnę VALUE_DROPOWN jako select list, a jej Type na SQL Query z kodem podobnym do poniższego. W sekcji Cascading List of Values ustawimy Parent Column na COLUMN_TYPE. Nie jest to idealny przykład, ale zadziała.

select display_value, return_value
from ( 
    select distinct 
        job as display_value,  
        job as return_value, 
        'job-dropdown' as dropdown_type 
    from emp
    union
    select distinct 
        dname as display_value, 
        dname as return_value, 
        'department-dropdown' as dropdown_type 
    from dept
)
where dropdown_type = :COLUMN_TYPE

Teraz trudniejszą częścią tego rozwiązania są Dynamic Actions. Najpierw potrzebujemy DA w punkcie inicjalizacji Rows (podobnie jak w poprzednim przykładzie), ale musimy również ustawić wiele akcji, aby przesłać wartość COLUMN_TYPE, a następnie odświeżyć kolumny grida za pomocą funkcji JavaScript setcolumns.

Skończyło się na czterech Dynamic Actions. Nie wszystkie są niezbędne i zależy to od tego, w którym miejscu wiersza znajduje się dynamiczny dropdown. Trudno jest jednak poprawnie przygotować komórki i listy rozwijane po wybraniu COLUMN_TYPE.

Dynamic Actions to połączenie submit values dla COLUMN_TYPE i wywoływania funkcji JavaScript setcolumns. Dodano jedną dodatkową funkcję, która wykonuje Set Focus na VALUE_VARCHAR – aby opuścić pole typu kolumny i przesłać wartość po zmianie.

Ekran pokazujący wartości.

Po dodaniu Dynamic Actions grid będzie wyglądał następująco.

Ekran pokazujący wynik.

Porządkowanie i CSS

I znowu to samo. Kroki porządkowe są takie same jak wcześniej. Zmieniamy nazwę kolumny wartości, która jest pierwsza od lewej, na Value i ukrywamy niepotrzebne kolumny za pomocą tego samego kodu CSS.

Ostateczny wynik wygląda tak:

Ekran pokazujący wynik.

Bezpieczeństwo i walidacje

Jeśli chodzi o dynamiczne typy kolumn, często nie można tak naprawdę korzystać z natywnych autoryzacji i walidacji. Ale nie w tym przypadku. Wszystkie kolumny mogą korzystać z autoryzacji, wszystkich warunków po stronie klienta/serwera, a także walidacji. Tych natywnych dla typu kolumny, ale także dodatkowych niestandardowych walidacji, które można dołączyć do kolumny interactive grid.

W najgorszym przypadku, gdy JavaScript i CSS zawiodą, wprowadzanie danych nadal będzie bezpieczne i przetworzy wszystkie walidacje. Nie ma tu żadnych workarounds ani kompromisów.

Gadanina jest tania, pokaż mi kod!

Aplikacja demonstracyjna z oboma przypadkami jest dostępna TUTAJ. Cały kod, którego użyłem w tym artykule, jest dostępny TUTAJ! Daj mi znać, co sądzisz o tym rozwiązaniu! I oczywiście dziękuję za lekturę. Jeśli interesują Cię podobne treści związane z APEX, sprawdź inne moje artykuły na tym blogu:

  1. How to easily parse JSON files into Oracle Database tables: A quick guide
  2. A beginner’s guide to using Chart Patterns in Oracle APEX
  3. Oracle Database 23ai: JSON-Relational Duality Views and the ORA-42647 error
  4. Oracle APEX Testing with Chrome Recorder, Puppeteer, and Lighthouse Audit
  5. Pretius Drawing Plugin: Free office layout planner for Oracle APEX apps

Szukasz firmy tworzącej oprogramowanie?

Pracuj z zespołem, który pomógł już dziesiątkom rynkowych liderów. Umów spotkanie, by dowiedzieć się:

  • Jak działają nasze produkty
  • Jak możesz oszczędzić czas i pieniądze
  • Czym nasze rozwiązania różnią się od konkurencji

Przebieg kontaktu z Pretius

Dbamy o bezpieczeństwo Twoich danych: Certyfikat ISO

Działamy zgodnie z normą ISO 27001, zapewniając najwyższy poziom bezpieczeństwa Twoich danych.
certified dekra 27001
© 2026 Pretius. All right reserved.