Ctools modal API. Работа с модальными окнами

Последнее время появляются все больше и больше сайтов с попапами. Благодаря Эрлу Майлзу мы имеем замечательный инструмент - Ctools, представляющий мощный API для друпалера. Сегодня мы рассмотрим modal API и научимся с ним работать.

В качестве примера будем открывать форму обратной связи в модальном окне. Но прежде чем начать, я быстренько пробегусь по командам AJAX-фреймворка.

  • ajax_command_after($selector, $html, $settings = NULL) – вставляет содержимое $html после селектора $selector;
  • ajax_command_alert($text) – выводит алерт с текстом $text;
  • ajax_command_append($selector, $html, $settings = NULL) – добавляет содержимое $html в конец элемента с селектором $selector;
  • ajax_command_before($selector, $html, $settings = NULL) – вставляет содержимое $html перед селектором $selector;
  • ajax_command_changed($selector, $asterisk = '') – помечает селектор $selector классом ajax-changed;
  • ajax_command_css($selector, $argument) – устанавливает css свойства $argument у элемента с селектором $selector;
  • ajax_command_data($selector, $name, $value) – изменяет внутренние данные элемента с селектором $selector;
  • ajax_command_html($selector, $html, $settings = NULL) – изменяет содержимое элемента с селектором $selector на содержимое $html;
  • ajax_command_insert($selector, $html, $settings = NULL) – вставляет содержимое $html в селектор $selector;
  • ajax_command_invoke($selector, $method, array $arguments = array()) – выполняет jQuery метод $method для элемента с селектором $selector;
  • ajax_command_prepend($selector, $html, $settings = NULL) – вставляет содержимое $html в начало элемента с селектором $selector;
  • ajax_command_remove($selector) – удаляет элемент с селектором $selector;
  • ajax_command_replace($selector, $html, $settings = NULL) – заменяет элемент с селектором $selector на содержимое $html;
  • ajax_command_restripe($selector) – обновляет классы odd/event у строк таблицы с селектором $selector;
  • ajax_command_settings($argument, $merge = FALSE) – передаёт данные в глобальный массив настроек Друпала Drupal.settings.

Это команды, которые предоставляет ядро Друпала. Модуль Ctools предоставляет дополнительные ajax команды. Для их использования необходимо вызвать функцию ctools_include('ajax'). Список этих команд:

  • ctools_ajax_command_attr($selector, $name, $value) – меняет атрибут $name на значение $value у элемента с селектором $selector;
  • ctools_ajax_command_redirect($url, $delay = 0, $options = array()) – отправляет клиента по адресу $url с задержкой $delay;
  • ctools_ajax_command_reload() – перезагружает текущую страницу;
  • ctools_ajax_command_submit($selector) – отправляет форму с селектором $selector.

Для работы с модальными окнами нам потребуются дополнительные ajax команды, которые также предоставляет модуль Ctools. Для их использования необходимо вызвать функцию ctools_include('modal'). Список этих команд:

  • ctools_modal_command_dismiss() – закрывает модальное окно;
  • ctools_modal_command_display($title, $html) – открывает модальное окно с заголовком $title и содержимым $html;
  • ctools_modal_command_loading() – показывает экран загрузки в модальном окне;
  • ctools_modal_form_wrapper($form_id, $form_state) – выводит форму $form_id в попапе.

Переходим к написанию модуля.

Шаг 1. Для начала нам нужно объявить страницу в hook_menu, к которой будет обращаться ajax:

/**
 *  Implementation of hook_menu()
 */
function example_menu() {
  $items = array();

  $items['example/%ctools_js/contact'] = array(
    'title' => 'Contact',
    'page callback' => 'example_contact_page_callback',
    'page arguments' => array(1),
    'access arguments' => array('access site-wide contact form'),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
  );

  return $items;
}

Шаг 2. Далее нам нужно в page callback написать открытие модального окна:

/**
 * @param null $js
 * @return array
 */
function example_contact_page_callback($js = NULL) {
  
  // Если у пользователя выключен JavaScript в браузере, то отправляют его на обычную форму обратной связи.
  if (!$js) {
    drupal_goto('contact');
  }
  
  // Подключаем библиотеку для работы с модальным окном с помощью ajax команд.
  ctools_include('modal');

  $form_state = array(
    'title' => t('Contact'), // Используется для заголовка модального окна.
    'ajax' => TRUE,
    'build_info' => array(
      'args' => array(),
    ),
  );
  
  // Подключаем файл, в котором описана функция формирующая форму.
  form_load_include($form_state, 'inc', 'contact', 'contact.pages'); 

  $commands = array();

  // Формируем форму в модальном окне.
  $commands = ctools_modal_form_wrapper('contact_site_form', $form_state);
  
  // Если форма была успешно отправлена, то выводим сообщение об этом и закрываем модальное окно.
  if (!empty($form_state['executed'])) {
    $commands = array();
     
    // Выводим сообщение об успешной отправке формы.
    $commands[] = ajax_command_html('#messages-wrapper', theme('status_messages'));

    // Закрываем модальное окно.
    $commands[] = ctools_modal_command_dismiss();
  }

  return array('#type' => 'ajax', '#commands' => $commands);
}

Шаг 3. Необходимо создать блок, в котором будет выводится ссылка, по клику на которую будет открываться модальное окно:

/**
 * Implements hook_block_info().
 */
function example_block_info() {
  $blocks['example_contact'] = array(
    'info' => t('Contact modal link'),
    'cache' => DRUPAL_NO_CACHE,
  );

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function example_block_view($delta = '') {
  $block = array();

  if ($delta == 'example_contact') {
    
    // В этой функции подключаются необходимые библиотеки, js и css файлы (если нужны), а так же устанавливаются 
    // настройки для модального окна. Эту функцию мы опишем позже
    _example_include_modal();

    $block['content'] = ctools_modal_text_button(t('Contact'), 'example/nojs/contact', t('Contact'), 'ctools-modal-example-contact-style');
  }

  return $block;
}

Шаг 4. Необходимо написать подключение всех нужных библиотек, js и css файлов (если нужны), а также установить настройки для модального окна в функции "_example_include_modal":

function _example_include_modal() {

  static $added = FALSE;
  if ($added == FALSE) {
    $added = TRUE;

    // Include the CTools tools that we need.
    ctools_include('modal');
    ctools_include('ajax');
    ctools_modal_add_js();
    
    // Создаем массив с настройками для модального окна.
    $example_style = array(
      'example-contact-style' => array(
        'modalSize' => array(
          'type' => 'fixed', // Тип модального окна. фиксированный или резиновый.
          'width' => 420, // Ширина модального окна.
          'height' => 'auto', // Высота модального окна.
        ),
        'modalOptions' => array(
          'opacity' => (float) 0.3, // Прозрачность фона.
          'background-color' => '#000000', // Цвет фона.
        ),
        'closeText' => '', // Текст для закрытия модального окна.
        'loadingText' => '', // Текст, отображаемый в момент загрузки модального окна.
        'animation' => 'fadeIn', // Эффект появления модального окна.
        'animationSpeed' => 'fast', // Скорость анимации.
      ),
    );

    // Подключаем настройки для модального окна.
    drupal_add_js($example_style, 'setting');
  }
}

В итоге мы получаем всплывающую в попапе форму обратной связи работающую без перезагрузки страницы.

P.S. Не забываем добавить нашему модулю зависимость от модулей Ctools и Contact.

Комментарии (79)

Аватар пользователя Алекс
Алекс

Можно не только стандартные формы так рендерить. Я например использовал ваш пример в Ubercart, когда клиен захотел менять аяксово атрибуты в корзине по клику на ссылку. А форма как понимаете там с динамическим id. Так что этот способ универсальный) Спасибо)

Аватар пользователя Benya
Benya

Супер! Но к слову сказать, аяксом можно не только формы рендерить, а вообще все, что душе угодно. И нет разницы, стандартный это элемент или нет.

Аватар пользователя Leo
Leo

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

Аватар пользователя Benya
Benya

Если высота модального окна фиксированная - то центрироваться должно как положено и проблема где то у вас. Если высота задана как 'auto', то необходимо написать маленький js, который корректно выровняет по центру

Аватар пользователя Leo
Leo

Да вы правы, а можете подсказать где можно посмотреть на пример такого js. Или хотя бы сказать в какую сторону копать.

Аватар пользователя Benya
Benya
(function ($) {
  Drupal.behaviors.example = {
    attach: function (context){
      var wt;

      if (self.pageYOffset) { // all except Explorer
        wt = self.pageYOffset;
      }
      else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
        wt = document.documentElement.scrollTop;
      }
      else if (document.body) { // all other Explorers
        wt = document.body.scrollTop;
      }

      // Fix CTools bug: calculate correct 'top' value.
      var mdcTop = wt + ($(window).height() / 2) - ($('#modalContent', context).outerHeight() / 2);
      $('#modalContent', context).css({ top : mdcTop });
    }
  }
})(jQuery);
Аватар пользователя Константин
Константин

Здрасьте.
У меня не работает. %ctools_js при параметре 'nojs' адресе стабильно возвращает 0 и функция перенапрявляет на статичную страницу. js в браузере включен. В чём может быть проблема?

Аватар пользователя Benya
Benya

Предполагаю, что где до допустили ошибки, как вариант, что то не подключили

Аватар пользователя Константин
Константин

Парадокс, но заработало после добавления к открывающей окно ссылке класса use-ajax.

Аватар пользователя Benya
Benya

Я же говорил, что забыл что-то :)

Аватар пользователя Иван
Иван

Приветсвую!
Подскажите, как-то открывать модальное окно по клику на лин созданный не через ctools_modal_text_button?
Создаю руками линк с теми-же классами, на странице где подключен сабжевый блок, но по клику он переходит на страницу, а не открывает модально.
Спасибо!

Аватар пользователя Benya
Benya

Добавляйте только класс, который говорит о названии нужного стиля для модального окна, например ctools-modal-example-contact-style и класс ctools-use-modal. Класс ctools-use-modal-processed должен добавиться автоматом.

Аватар пользователя Angarsky
Angarsky

Вот тут хороший код для выравнивания окон, а то твой пример в ФФ у меня дергался и не всегда верно срабатывал ;)

Аватар пользователя Benya
Benya

попробую как пригодится :) Раньше проблем не наблюдал, но использовал давно, мб что то поменялось в последних версиях браузера

Аватар пользователя Виктория
Виктория

Здравствуйте! У меня почему-то пустое модальное окно со значком загрузки и больше ничего не появляется. в чем проблема?

Аватар пользователя Benya
Benya

Скорее всего у вас допущена ошибка в php. Проверьте логи

Аватар пользователя Виктория
Виктория

Доброго времени суток! Подскажите, а как сделать, чтобы в модальном окне отобржалась собственная форма?

Аватар пользователя Benya
Benya

Что значит собственная форма, поясните?

Аватар пользователя Виктория
Виктория

На примере вашего модуля в модальном окне будет отображена стандартная форма "contact". а как создать свою форму со своими кнопками и полями и её отобразить в  модальном окне?  я новичок, поясните пожалуйста

Аватар пользователя Benya
Benya

Нужно первым параметром передавать id формы здесь: $commands = ctools_modal_form_wrapper('contact_site_form', $form_state); И если форма объявлена в *.module то эта строка не нужна, в противном случае нужно подправить: form_load_include($form_state, 'inc', 'contact', 'contact.pages');

Аватар пользователя алексей
алексей

Обнаружил конфликт с модулем modal_form. если в включено отображение формы контактов в модальном окне выкидывает ошибку

Аватар пользователя Benya
Benya

А зачем два модуля, которые делают одно и тоже?

Аватар пользователя Саша
Саша

Такая ситуация: все вроде бы получилось по вашему уроку и контактная форма есть, но по нажатию на кнопку, когда все поля формы заполнены  ничего не происходит, хотя функция валидации срабатывает. В чем может быть дело?

Аватар пользователя Benya
Benya

а форма отправляется после валидации? Нет ли ошибок в консоли?

Аватар пользователя Саша
Саша

А как это проверить? да, отправляется

Аватар пользователя Benya
Benya

Открыть в любом браузере консоль и посмотреть, нет ли ошибок при открытии модального окна и при отправлении формы. Как открыть консоль - можно нагуглить:)

Аватар пользователя Саша
Саша

Но ведь функция валидации срабатывает или это в данном случае не важно?

Аватар пользователя Benya
Benya

Так модальное окно закрывается же не функцией валидации, а js'ом при успешном сабмите формы

Аватар пользователя Саша
Саша

Оно и закрывается. А как сделать, чтобы в модальном окне хотя бы сообщение появилось, а потом его можно было закрыть?

Аватар пользователя Benya
Benya

Смотреть ajax commands и писать код. Пример можно глянуть в модуле Ajax Login/Register, там если не ошибаюсь в сообщение показывается в модальном окне и потом происходит перезагрузка

Аватар пользователя Саша
Саша

Все-таки не до конца еще понятно)) У меня есть форма, которая описана в отдельном файле, а потом подключается в той строке, где у вас подключается форма "contact". Вот. Я могу прописать в том же файле  с формой обработчик её кнопки? (сначала я так и сделала, но окно просто закрывается). И как я в данной ситуации могу использовать  ajax commands?

Аватар пользователя Benya
Benya

Например, если ты хочешь показывать сообщение в модальном окне и что бы оно не закрывалось автоматом, то нужно убрать из кода эту команду $commands[] = ctools_modal_command_dismiss(), а так же поправить селектор, в который вставляется сообщение в этой команде $commands[] = ajax_command_html('#messages-wrapper', theme('status_messages')). Возможно потребуется другая команда, зависит от твоей разметки

Аватар пользователя Павел
Павел

А как получить форму создания entity bundle из модуля eck в ctools modal? второй день бьюсь и не получается

Аватар пользователя Павел
Павел

Конкретнее возникает такая ошибка при вызове модального окна

Возникла AJAX HTTP ошибка.
Полученный код HTTP: 200
Следует отладочная информация.
Путь: /doc/ajax/add/
Текст Состояния: OK
Текст Ответа: 
( ! ) Fatal error: Call to a member function entityType() on a non-object in /drupal/sites/all/modules/eck/eck.entity.inc on line 359
Call Stack
#TimeMemoryFunctionLocation
10.0001317800{main}(  )../index.php:0
20.143712816936menu_execute_active_handler(  )../index.php:21
30.143812817864call_user_func_array
(  )../menu.inc:519
40.143812818280modalreg_modal_callback(  )../menu.inc:519
50.168312864560ctools_modal_form_wrapper(  )../modalreg.module:62
60.168312864960drupal_build_form(  )../modal.inc:222
70.168312867368drupal_retrieve_form(  )../form.inc:350
80.174313187920call_user_func_array
(  )../form.inc:841
90.174313188464eck__entity__form(  )../form.inc:841
Аватар пользователя Benya
Benya

У меня все нормально, для теста создал сущность product и бандл simple:

ctools_include('modal');

$entity_type_name = 'product';
$bundle_name = 'simple';

$entity_type = entity_type_load($entity_type_name);
$bundle = bundle_load($entity_type_name, $bundle_name);
$entity = entity_create($entity_type->name, array('type' => $bundle->name));

$form_state = array(
  'ajax' => TRUE,
  'build_info' => array(
    'args' => array($entity),
  ),
);

form_load_include($form_state, 'inc', 'eck', 'eck.entity');
$commands = ctools_modal_form_wrapper("eck__entity__form_add_{$entity_type_name}_{$bundle_name}", $form_state);

if (!empty($form_state['executed'])) {
  $commands = array();
  $commands[] = ctools_modal_command_dismiss();
}
return array('#type' => 'ajax', '#commands' => $commands);
Аватар пользователя Павел
Павел

Спасибо! нашёл ошибку у себя!

Аватар пользователя Виктория
Виктория

Здравствуйте! Что можно сделать, если форма в модальном окне загружается  очень долго? Как можно исправить?

Аватар пользователя Benya
Benya

Нужно анализировать почему медленно грузится. Может быть код плохой...

Аватар пользователя Виктория
Виктория

А еще такой вопрос: как изменить размер модального окна?

Аватар пользователя Benya
Benya

Смотрите Шаг 4 в этом уроке, а именно код и комментарии к нему

Аватар пользователя Виктория
Виктория

В том то и дело, что выполнила все как у Вас. Ссылка по которой вызывается модальное окно выглядит так:
'class' => array('ctools-modal-ajax_publish-contact-style ctools-use-modal')
где ajax_publish-contact-style - стиль, в котором заданы параметры как в шаге 4. В чем ошибка?

Аватар пользователя Benya
Benya

ну так меняйте в массиве настройки модального окна. Если все сделали как описано - должно работать

Аватар пользователя Виктория
Виктория

а класс указан верно? я меняла ширину для примера и высоту, но изменений не было

Аватар пользователя Benya
Benya

Вроде да, возможно только в массиве не так указан.

Аватар пользователя Виктория
Виктория

Если не сложно, посмотрите код пожалуйста, может заметите, что не так:

$ajax_publish_style = array(
  'ajax_publish-contact-style' => array(
    'modalSize' => array(
      'type' => 'fixed', // Тип модального окна. фиксированный или резиновый.
      'width' => '100', // Ширина модального окна.
      'height' => '200', // Высота модального окна.
    ),
    'modalOptions' => array(
      'opacity' => (float) 0.3, // Прозрачность фона.
      'background-color' => '#000000', // Цвет фона.
    ),
    'closeText' => '', // Текст для закрытия модального окна.
    'loadingText' => '', // Текст, отображаемый в момент загрузки модального окна.
    'animation' => 'fadeIn', // Эффект появления модального окна.
    'animationSpeed' => 'fast', // Скорость анимации.
  ),
  'modalTheme' => 'ajax_publish_modal', // Имя темы модального окна
);

// Подключаем настройки для модального окна.
drupal_add_js($ajax_publish_style, 'setting');

Далее ссылка, которая выводится под комментарием:

$comment->content['links']['comment']['#links']['comment-publish'] = array(
  // полное содержимое комментария
  'title' => 'Оценить решение',
  'href' => 'ajax_publish/nojs/contact/'. $comment->cid,
  'html' => TRUE,
  'attributes' => array(
    'class' => array('ctools-modal-ajax_publish-contact-style ctools-use-modal'),
  ),
);
Аватар пользователя Benya
Benya

у вас форма вообще загружается в модальное окно или нет?

Аватар пользователя Виктория
Виктория

Да. Форма отображается

Аватар пользователя Виктория
Виктория

Можно ли как-то исправить ситуацию, Вы не подскажите?

Аватар пользователя Benya
Benya

смотрите консоль, может есть ошибки и в какой то момент прерывается выполнение. С виду вроде бы правильно

Аватар пользователя Павел
Павел

Доброго дня.
Есть вопрос.

Есть список, который состоит из ссылок. Каждая ссылка - это вызов определенного ctools modal окна.
Есть кнопка, которая обновляет список (с помощью ajax догружает новые элементы - такие ссылки).
Как сделать так, что бы вызов ctools modal работал для новых загруженных ссылок?
Те ссылки, которые загружаюстся в момент загрузки страницы - работают ок. Те, что с помощью аякс - нет.

Я почему-то думал, что обработчик от ctools автоматически подхватывает ссылки. А оказалось, что нет. При том новая инициализация свойст модального окна на момент рендела элементов списка к успеху не привел.

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

Заранее спасибо.

Аватар пользователя Benya
Benya

Здравствуйте. Покажите пример, как вы выводите ссылки. Должно все работать если правильно сделали, лично проверил.

Аватар пользователя Бабайка
Бабайка

А вот вопрос, бьюсь который день. После вывода формы в модальном окне CTools как сделать какой-либо элемент формы активным (focus()). Инлайновый js в хуке form_alter dsltn ошибку в консоли Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/. Может есть более простой способ? 

Аватар пользователя Бабайка
Бабайка

Спасибо, попробую. Но вот вопрос о добавлении js в ctools modal формы все равно открыт.
Я экспериментально понял так, что ошибку выдает не мой скрипт, а при наличии моего скрипта (любого) ошибку выдает какой-то из скриптов CTools. Кстати, что интересно, alert() отрабатывает. Несмотря на ошибку. А вот focus() не хочет.

Аватар пользователя Бабайка
Бабайка

Спасибо =)) По-моему то что надо. Сейчас еще покумекаю в каком месте хук перехватывать

Аватар пользователя Денис
Денис

Добрый день!
Подскажите, пожалуйста, как при добавлении ссылки для вызова модального окна методом ctools_modal_text_button() добавить к этой ссылке onclick?

Аватар пользователя Benya
Benya

Никак, используйте функцию l()

Аватар пользователя Макс
Макс

Здравствуйте.
Будьте добры, подскажите, как как modal popup window добавить свой код javascript? Что бы было понятнее о чем вопрос опишу задачу - в поле для ввода телефона с помощью jQuery Masked Input задаётся маска ввода телефона.
p.s. JS читать могу, но дебри внутренностей Друпала уничтожают мой мозг, пните меня в нужную сторону, будьте добры :)

Аватар пользователя Макс
Макс

Решил проблему так: подключил в свой модуль файл скрипта с таким содержанием

Drupal.behaviors.telephoneMask = {
  attach: function () {
    $(".telephoneMask").mask("+7-999-999-99-99",{placeholder:" "});
  }
}

вдруг кто захочет повторить - нужно подключить так же jquery.maskedinput-1.3.js

Аватар пользователя Benya
Benya

Я бы еще контекст добавил, например 'form'

Аватар пользователя shumod
shumod

Проверьте, чтобы было $commands[], а не $commands

Аватар пользователя Михаил
Михаил

У меня аналогичная проблема, ссылки, добавленные через ajax, не обрабатываются ctool'ом и не получают класс ctools-use-modal-processed. А соответственно и не открываются.

Новая ссылка добавляется при сабмите формы в модальном окне.

function agro_recipe_weeds_form_submit($form, &$form_state) {
  ...
  $link_weed = l('Новая ссылка', 'new-link/nojs', array(
    'attributes' => array(
      'class' => array('ctools-use-modal'),
     )
  ));

  $form_state['ajax_commands'][] = ctools_modal_command_dismiss();
  $form_state['ajax_commands'][] = ajax_command_html('.weeds', $link_weed);
}
Аватар пользователя Николай
Николай

Подскажите, что может быть при создании формы через Webform и вывода ее через Modal Form, после отправки сообщения кнопка  "Отправить" меняется на "Сохранить", и только после нее делает редирект на нужную страницу, как убрать эту промежуточную кнопку?

Аватар пользователя Михаил
Михаил

Для корректной обработки ctool'ом таких ссылок их необходимо оборачивать в div. Работать будет в следующий код:

$form_state['ajax_commands'][] = ajax_command_html('.weeds', '<div>' . $link_weed . '</div>');
Аватар пользователя Михаил
Михаил
$form_state['ajax_commands'][] = ajax_command_html('.weeds', '<div>' . $link_weed . '</div>');
Аватар пользователя Товарищ
Товарищ

А где должны размещаться эти примеры кода?

Аватар пользователя wolf
wolf

Добрый день, подскажите пожалуйста в модальном окне вывожу список элементов (сообщения) и форму для их добавления на список сообщений "повешен" при заполнении формы сообщение отображается в списке с  помощью команды ajax_command_append все работало как надо пока не "повесил" плагин скроллинга на список сообщений (он добавляет свои <div> в внутри списка сообщений) надо чтобы новые сообщения добавлялись в ту область которая скроллится но команда ajax_command_append  не видит создаваемых плагином div

Аватар пользователя Benya
Benya

Вы хоть бы запятые поставили, трудно понять, что нужно сделать

Аватар пользователя Max Lipin
Max Lipin

Спасибо за подробный HOWTO. У меня почему-то не выводятся сообщения об удачной отправке.

Подскажите, как реализовать после удачной отправки:
- изъять (или скрыть) саму форму, вывести сообщение пользователю, что письмо ушло. При этом окно остается открытым.

При закрытии окна вне зависимости была форма отправлена или возвращена с ошибками - выполнить ребилд (reset) и вернуть "чистую" форму.
 

Аватар пользователя Benya
Benya

> - изъять (или скрыть) саму форму, вывести сообщение пользователю, что письмо ушло. При этом окно остается открытым.

Смотрите AJAX commands. В двух словах, необходимо убрать комманду закрытия окна и добавить команду, которая будет скрывать форму.

> При закрытии окна вне зависимости была форма отправлена или возвращена с ошибками - выполнить ребилд (reset) и вернуть "чистую" форму.

Не совсем понял смысл этого предложения, получается если пользователь закрывает окно, вы хотите ему заново открыть  чистую форму? Тоесть лишая тем самым пользователя закрыть окно?

Аватар пользователя Max Lipin
Max Lipin

Попытка номер три (набрал в блокноте), т.к. ваша форма комментариев сбрасыват поля при неверно введеном коде. Это так, на заметку.

В функции _page_callback:  

if (!empty($form_state['executed']) && !form_get_errors()) {
  $commands[] = ajax_command_remove('#saf-form');
}

Теперь сообщения перестали выводится. Но вызывал кастомную форму, объявленную в этом же модуле, поэтому дописал ей ajax_callback, в случае неверно заполненных полей возвращиаем форму с ошибками:

/**
 * Implements ajax callback
 */
function mymodule_form_ajax_callback($form, $form_state){
  $commands = array();
  if (!form_get_errors()) {
    drupal_set_message('Well done!');
        
    $commands[] = ajax_command_remove('#saf-form');
    $commands[] = ajax_command_html('#modal-content', theme('status_messages'));

    return array('#type' => 'ajax', '#commands' => $commands);
  }
  else {
    return $form;
  }
}

Что получилось:
- при успешной отправке - скрывается форма и выводится сообщение "Well done!";
- при повторном вызове формы - форма собирается вновь, что избавило от необходимости очистки.
- при неверно введенных данных - возвращается форма с подсветкой ошибочных полей без сброса.
- при закрытии - форма удаляется.

Осталось дописать функцю валидации и все это темизировать. Нужно будет проверить работу без JS.

Аватар пользователя Benya
Benya

Вы явно что то делаете не так. Формы валидироваться должны по умолчанию, без дополнительного кода на ajax_callback. Тоже самое при повторном вызове формы, в открывашемся попапе - новый экземпляр формы

Аватар пользователя Max Lipin
Max Lipin

Да, вы правы. Мой вариант оказался избыточен, и отправка формы происходит в два запроса. Калбэк:

function saf_page_callback($js = NULL) {
  if (!$js) {
    drupal_goto('contact');
  }

  ctools_include('modal');

  $form_state = array(
    'title' => t('Add question'), 
    'ajax' => TRUE,
    'build_info' => array(
      'args' => array(),
    ),
  );

  $commands = array();

  $commands = ctools_modal_form_wrapper('saf_form', $form_state);
  
  if (!empty($form_state['executed']) && !form_get_errors()) {
    $commands[] = ajax_command_remove('#saf-form');
    $commands[] = ajax_command_html('#modal-content', theme('status_messages'));
  }

  return array('#type' => 'ajax', '#commands' => $commands);  
}

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