APEX: как реализовать круговые каскадные списки выбора (многие ко многим) - PullRequest
3 голосов
/ 13 марта 2020

Когда вы имеете дело со связями «многие ко многим», как реализовать круговую каскадную логику c для списков выбора?

В качестве примера я создал простое тестовое приложение, которое отслеживает книги и авторов.
(Это намного проще, чем мой настоящий бизнес-сценарий, и он показывает проблему более четко.)

Домашняя страница содержит:

  • кнопку, которая передает страницу
  • список выбора для книг
  • список выбора для авторов

Я использую кнопку отправки, потому что в моем реальном бизнес-сценарии очень долго отчет, который занимает 20-60 секунд, чтобы обновить sh - и есть дюжина списков выбора, из которых пользователь должен выбрать, ДО отправка страницы и получение отчета.

enter image description here

enter image description here enter image description here

Вот полный сценарий, содержащий все тестовые данные, с которыми я работаю:

CREATE table "BOOK" (
    "ID"         INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL ENABLE,
    "TITLE"      VARCHAR2(100) NOT NULL ENABLE,
    constraint  "BOOK_CK" check ("TITLE"<>''),
    constraint  "BOOK_PK" primary key ("ID"),
    constraint  "BOOK_UK1" unique ("TITLE")
)
/

CREATE table "AUTHOR" (
    "ID"         INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL ENABLE,
    "NAME"       VARCHAR2(100) NOT NULL ENABLE,
    constraint  "AUTHOR_CK" check ("NAME"<>''),
    constraint  "AUTHOR_PK" primary key ("ID"),
    constraint  "AUTHOR_UK1" unique ("NAME")
)
/

INSERT INTO BOOK (TITLE) VALUES ('BOOK01');
INSERT INTO BOOK (TITLE) VALUES ('BOOK02');
INSERT INTO BOOK (TITLE) VALUES ('BOOK03');
INSERT INTO BOOK (TITLE) VALUES ('BOOK04');
INSERT INTO BOOK (TITLE) VALUES ('BOOK05');
INSERT INTO BOOK (TITLE) VALUES ('BOOK06');
INSERT INTO BOOK (TITLE) VALUES ('BOOK07');
INSERT INTO BOOK (TITLE) VALUES ('BOOK08');
INSERT INTO BOOK (TITLE) VALUES ('BOOK09');
INSERT INTO BOOK (TITLE) VALUES ('BOOK10');
INSERT INTO BOOK (TITLE) VALUES ('BOOK11');
INSERT INTO BOOK (TITLE) VALUES ('BOOK12');
INSERT INTO BOOK (TITLE) VALUES ('BOOK13');
INSERT INTO BOOK (TITLE) VALUES ('BOOK14');
INSERT INTO BOOK (TITLE) VALUES ('BOOK15');
INSERT INTO BOOK (TITLE) VALUES ('BOOK16');
INSERT INTO BOOK (TITLE) VALUES ('BOOK17');
INSERT INTO BOOK (TITLE) VALUES ('BOOK18');
INSERT INTO BOOK (TITLE) VALUES ('BOOK19');
INSERT INTO BOOK (TITLE) VALUES ('BOOK20');

INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR01');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR02');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR03');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR04');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR05');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR06');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR07');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR08');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR09');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR10');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR11');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR12');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR13');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR14');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR15');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR16');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR17');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR18');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR19');
INSERT INTO AUTHOR (NAME) VALUES ('AUTHOR20');

CREATE table "BOOK_AUTHOR" (
    "ID"         INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL ENABLE,
    "BOOK_ID"    INTEGER NOT NULL ENABLE,
    "AUTHOR_ID"  INTEGER NOT NULL ENABLE,
    constraint  "BOOK_AUTHOR_PK" primary key ("ID"),
    constraint  "BOOK_AUTHOR_UK1" unique ("BOOK_ID","AUTHOR_ID")
)
/

ALTER TABLE BOOK_AUTHOR ADD FOREIGN KEY (BOOK_ID)
      REFERENCES BOOK (ID) ENABLE
/

ALTER TABLE BOOK_AUTHOR ADD FOREIGN KEY (AUTHOR_ID)
      REFERENCES AUTHOR (ID) ENABLE
/

CREATE OR REPLACE VIEW "VW_BOOK_AUTHOR" AS
SELECT ba.ID,
       ba.BOOK_ID,
       b.TITLE BOOK_TITLE,
       ba.AUTHOR_ID,
       a.NAME AUTHOR_NAME
FROM BOOK_AUTHOR ba
LEFT JOIN BOOK b on b.ID = ba.BOOK_ID
LEFT JOIN AUTHOR a on a.ID = ba.AUTHOR_ID
/

INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK01' AND a.NAME='AUTHOR01';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK02' AND a.NAME='AUTHOR02';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK03' AND a.NAME='AUTHOR03';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK04' AND a.NAME='AUTHOR04';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK05' AND a.NAME='AUTHOR05';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK06' AND a.NAME='AUTHOR06';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK07' AND a.NAME='AUTHOR07';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK08' AND a.NAME='AUTHOR08';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK09' AND a.NAME='AUTHOR09';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK10' AND a.NAME='AUTHOR10';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK11' AND a.NAME='AUTHOR11';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK12' AND a.NAME='AUTHOR12';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK13' AND a.NAME='AUTHOR13';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK14' AND a.NAME='AUTHOR14';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK15' AND a.NAME='AUTHOR15';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK16' AND a.NAME='AUTHOR16';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK17' AND a.NAME='AUTHOR17';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK18' AND a.NAME='AUTHOR18';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK19' AND a.NAME='AUTHOR19';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK20' AND a.NAME='AUTHOR20';

INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK02' AND a.NAME='AUTHOR01';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK02' AND a.NAME='AUTHOR03';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK04' AND a.NAME='AUTHOR03';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK04' AND a.NAME='AUTHOR05';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK06' AND a.NAME='AUTHOR05';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK06' AND a.NAME='AUTHOR07';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK08' AND a.NAME='AUTHOR07';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK08' AND a.NAME='AUTHOR09';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK10' AND a.NAME='AUTHOR09';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK10' AND a.NAME='AUTHOR11';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK12' AND a.NAME='AUTHOR11';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK12' AND a.NAME='AUTHOR13';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK14' AND a.NAME='AUTHOR13';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK14' AND a.NAME='AUTHOR15';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK16' AND a.NAME='AUTHOR15';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK16' AND a.NAME='AUTHOR17';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK18' AND a.NAME='AUTHOR17';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK18' AND a.NAME='AUTHOR19';
INSERT INTO BOOK_AUTHOR (BOOK_ID,AUTHOR_ID) SELECT b.ID, a.ID FROM BOOK b, AUTHOR a WHERE b.TITLE='BOOK20' AND a.NAME='AUTHOR19';

На этом этапе, если вы выберете книгу, и ru Нажмите на кнопку, чтобы отправить страницу, список авторов сужается только до авторов выбранной книги. (И наоборот, если вы выбираете автора.)

Для следующего шага мне нужно обновить sh списки выбора БЕЗ , отправив и обновив всю страницу.

Давайте начнем с добавления настроек каскада только к ОДИН из списков выбора (P1_AUTHOR):

enter image description here

Пока что так хорошо.
Теперь список выбора автора автоматически обновляется, когда пользователь выбирает книгу.

Однако мы сталкиваемся с проблемами при настройке параметров каскада для другого списка выбора:

enter image description here

Теперь, когда мы запускаем страницу и пытаемся выбрать книгу ИЛИ автора, страница падает с исключением Javascript:

enter image description here

enter image description here

Ошибка Maximum call stack size exceeded, что указывает на бесконечное выполнение l oop (с рекурсией ).

Кажется, что COULD может быть ошибкой / недосмотром в коде APEX, поскольку техническую ситуацию легко разрешить с помощью правильного / robu st logi c.

Тем не менее, как я могу обойти это?
Есть ли другой способ настройки параметров каскада, чтобы это работало?
Или я могу написать собственную динамику c действия и Javascript, чтобы вручную отправить отдельные элементы и вручную обновить sh списки выбора?

1 Ответ

3 голосов
/ 14 марта 2020

Прежде всего, я просто хочу сказать, способ задать вопрос! :)

Как вы обнаружили, «каскадная» часть предназначена для передачи от родителя к потомку, а не по кругу (что может привести к переполнению стека! Извините, не устояла).

Я дам вам решение, но сразу признаю, что оно не оптимально в том смысле, что для каждого изменения списка выбора потребуется два Ajax вызова, а не один. Я подниму вопрос с командой APEX в надежде, что они могут решить ее в будущем.

Я начну шаги, предполагая, что люди запустили ваш скрипт и имеют пустую страницу с HTML область. На моей странице было 53, поэтому люди должны будут внести соответствующие изменения.

Часть 1: Основы

  1. Добавить элемент страницы в регион , Установите Имя на P53_BOOK , Тип на Выберите Список и Список значений Введите на SQL Запрос . Введите следующий код в поле SQL Запрос :

    select title d,
      id r
    from book
    where (
      :P53_AUTHOR is null
        or id in (
          select book_id
          from book_author
          where author_id = :P53_AUTHOR
        )
    )
    order by title
    
  2. Добавьте еще один элемент в регион. Установите Имя на P53_AUTHOR , Тип на Выберите Список и Список значений Введите на SQL Запрос . Введите следующий код в поле SQL Запрос :

    select name d,
      id r
    from author
    where (
      :P53_BOOK is null
        or id in (
          select author_id
          from book_author
          where book_id = :P53_BOOK
        )
    )
    order by name
    
  3. На панели рендеринга конструктора страниц щелкните правой кнопкой мыши P53_BOOK , выберите Создать Dynami c Действие , а затем установите его Имя в P53_BOOK изменено . Установите Клиентское условие Тип на JavaScript Выражение и введите следующий код в поле JavaScript Выражение :

    this.browserEvent.originalEvent !== undefined
    

    Хотя это выглядит странно, это предотвратит запуск действия Dynami c при обновлении другого элемента (предотвращает другую проблему с циклической логикой c; подробности см. В ).

  4. На уровне действия измените Действие с Показать на Выполнить PL / SQL Код . Установите PL / SQL Код на null; и Элементы для отправки на P53_BOOK . Это действие используется только для обновления состояния сеанса для P53_BOOK до действия refre sh, которое вы создадите следующим.

  5. Добавьте новое действие, которое запускается после выполнения кода PL / SQL. действие. Установите Действие на Refre sh, Тип выбора на Элемент (ы) и Элемент (ы) до P53_AUTHOR .
  6. Щелкните правой кнопкой мыши P53_AUTHOR , выберите Создать Dynami c Действие , а затем установите его Имя в P53_AUTHOR изменено . Установите Клиентское условие Тип на JavaScript Выражение и введите следующий код в поле JavaScript Выражение :

    this.browserEvent.originalEvent !== undefined
    
  7. На уровне действия измените Действие на Выполнить PL / SQL Код . Установите PL / SQL Код на null; и Элементы для отправки на P53_AUTHOR .

  8. Добавьте новое действие, которое срабатывает после действия Выполнить код PL / SQL. Установите Действие на Refre sh, Тип выбора на Элемент (ы) и Элемент (ы) до P53_BOOK .

Если вы запустите это, оно должно работать как положено (по большей части). Самая большая проблема заключается в том, что если вы внесете изменение в один элемент, и он обновит другой элемент, выбор в другом элементе всегда будет потерян - даже если после элемента refre * 1276 в элементе существует значение pre-refre sh. *.

Следующие шаги могут быть использованы для автоматического повторного выбора значения, если оно доступно.

Часть 2. Восстановление ранее выбранных значений (необязательно)

  1. Go в P53_BOOK изменено Dynami c Действие. Добавьте новое истинное действие, которое срабатывает перед двумя предыдущими действиями. Установите Действие на Выполните JavaScript Код и введите следующий код в поле Код :

    $('#P53_BOOK').data('last-val', $v('P53_BOOK'));
    

    Этот код использует jQuery s data метод для хранения значения элемента pre-refre sh. Вы будете использовать его позже после того, как refre sh.

  2. Go в P53_AUTHOR изменилось Dynami c Action. Добавьте новое истинное действие, которое срабатывает перед двумя предыдущими действиями. Установите Действие на Выполните JavaScript Код и введите следующий код в поле Код :

    $('#P53_AUTHOR').data('last-val', $v('P53_AUTHOR'));
    
  3. Щелкните правой кнопкой мыши P53_BOOK и выберите Создать Dynami c Действие . Установите Имя на P53_BOOK обновлено и установите Событие на После обновления sh.

  4. На уровне действия установите Действие на Выполнить JavaScript Код и введите в поле Код следующее:

    $s('P53_BOOK', $('#P53_BOOK').data('last-val'), null, true);
    

    Этот код устанавливает значение P53_BOOK с помощью метода data (на этот раз как получатель). Последний параметр $s (true) передается для подавления события изменения, которое в противном случае создало бы более циклические логики c.

  5. Щелчок правой кнопкой мыши P53_AUTHOR и выберите Создать Dynami c Действие . Установите Имя на P53_AUTHOR обновил и установите Событие на После обновления sh.
  6. На Уровень действия, установите Действие на Выполните JavaScript Код и введите в поле Код следующее:

    $s('P53_AUTHOR', $('#P53_AUTHOR').data('last-val'), null, true);
    

Это должно сделать это. Но осталась еще одна досадная вещь ... Если вы выберете нулевое значение в любом из полей, вы можете ожидать, что оно обновит sh в обоих полях (показывая все опции для обоих), но это не сработает. Если это желательно, я бы рекомендовал просто добавить отдельную кнопку сброса. В противном случае, вам, вероятно, лучше написать все это в необработанном JavaScript коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...