AjaxDispatcher @deprecated since 1.5.11
AjDi @since 1.5.11
Описание

Функциональный модуль AjDir связывает серверный backend и браузерный frontend.

Он позволяет загружать компоненты xTables и другое содержимое на страницу через ajax запросы в ajax контейнерах.

Каждый ajax контейнер имеет свое вероятностное уникальное имя и связан с одним или несколькими селекторами страницы. Он может загружать за раз как один, так и множество компонентов и содержимого, автоматически обновлять данные через заданный промежуток времени при необходимости.

Также он позволяет загружать данные с других доменов в режиме кросс-доменных ajax запросов.

При загрузке ajax dispatcher осуществляется его связка с селекторами, которые он обслуживает. В последующем возможен вызов ajax dispatcher, например, для его перезагрузки или подгрузки в селектор другого содержимого, через название селектора. Если в том же селекторе регистрируется другой ajax dispatcher, то старый удаляется.

Модуль AjDi состоит из класса xtables\base\AjDi и js функий, располагаемых в файле xtables.js.

@since version 1.7.22

JS функции, обслуживающие ajDi, могут получать данные локально из функций, для чего вместо href и data-url, нужно использовать параметр target (подробнее) и data-target (подробнее).

Использование

На сервере могут быть использованы следующие методы:

getStart(array $params): string

Помещает js код с вызовом ajax dispatcher в нужном метсе страницы. В метод нужно передать массив именованных параметров:

  • string url - обязательно роут, по которому со страницы будет сделан запрос через ajax для загрузки содержимого;;
  • array data - необязательно ассоциативный массив с параметрами, которые должны быть переданы в аякс запросе;
  • int refresh - необязательно время в секундах для обновления содержимого аякс контейнера;
  • array|string reload_bundles - необязательно простой массив с именами селекторов (['#some', '#some2']) или строка с именами селекторов через запятую;
  • string history - необязательно адрес для добавления история для аякс навигации.

use xtables\base\AjDi;

AjDi::getStart([
    'url' => '/incoming/ajax-data',
    'data' =>  [
        'some' => 'val',
        'some1' => 'val2'
    ],
    'refresh' => 300,
    'reload_bundles' => ['#some-selector', '#else-selector'],
    'history' => '/some/new/url'
]);

getJs(string $url, array $params = [], int $updateTimeSec = 0): string

Упрощенный (более старый) метод. Помещает js код с вызовом ajax dispatcher в нужном месте страницы. В данный метод передаются:

  • $url - обязательно роут, по которому со страницы будет сделан запрос через ajax для загрузки содержимого;
  • $params - необязательно могут быть переданы параметры в массиве, тогда они будут переданы на страницу загрузки, указанную в url (по умолчанию пустой массив);
  • $updateTimeSec - необязательно может быть указано количество секунд, по истечение которых ajDi должен обновлять содержимое (по умолчанию стоит значение ноль и перезагрузка не осуществляется);

use xtables\base\AjDi;

AjDi::getJs('/incoming/ajax-data', $this->params, 300);

getReply(array $reply): string

После помещения js кода нужно создать роут с методом, который будет соответствовать url и будет отдавать содержимое по запросу от ajDi.
В методе модели, к которой будет обращаться метод контроллера, нужно вызвать метод xtables\base\AjDi::getReply($reply), в который должен быть передан ассоциативный массив.
Метод возвратит отформатированный для отправки ответ.
Передаваемый в метод массив $reply должен состоять из:

'css_selector' => 'value'

ключей, соответствующих элементам DOM, в которые должно быть загружено содержимое; элементы должны именоваться по правилам css: класс как '.some-class', идентификатов как '#some-id'
в качестве значений нужно добавить содержимое, в том числе html, которое будет вставлено в элемент DOM, соответствующий наименованию ключа массива


'#some-id' => 'some value',
'#else-id' => '<p>else value</p>'

AjDi::INFOBLOCK

Если необходимо добавить элемент в верхний инфоблок (по умолчанию расположен вверху справа в системном лейауте xtables-basic, который будет скрываться после перезагрузки через аякс диспетчеры, то необходимо передать второй параметр:

  • можно передать строку с содержимым, которое будет добавлено в инфоблок, данный элемент будет скрыт после изменения url адреса;
  • можно передать массив с параметрами или массив массивов, в каждом из которых будет присутсвовать:
    • content - обязательно содержимое элемента инфоблока;
    • limit - необязательно если данный параметр не определен, то действие элемента инфоблока будет распространяться только на текущий url;
      если в качестве значение данного параметра указать any, то элемент инфоблока не будет скрыт после изменения url и будет оставаться, пока не будет полностью перезагружена страница;
    • name - необязательно по умолчанию, если не передать имя в качестве параметра, то для идентификации элемента инфоблока будет использоваться имя ajDi, которое определяется динамически и может изменяться; для фиксации имени инфоблока можно определить данный параметр с любым подходящим именем, это имя будет добавлено в css id элемента, соответственно имя не должно содержать пробелов.

Содержимое инфоблока, чтобы корректно отображаться в общей ленте, должно быть сформировано по следующему примеру:


<a href="#" class="dropdown-toggle" data-toggle="dropdown">
    <!-- some icon or other content -->
</a>
<ul class="dropdown-menu dropdown-menu-right" style="width: 300px; max-height: 300px; overflow-y: auto">
    <li style="width: 100%">
        <!-- dropdown item -->
    </li>
</ul>

AjDi::INFOBLOCK => [
    'limit' => 'any',
    'name' => 'some-name',
    'content' => 'content'
]

AjDi::IMPORT

Если для обработки ответа ajDi нужен дополнительный js и/или css файлы, то они могут быть подгружены. Для этого нужно определить параметр AjDi::IMPORT с массивом файлов:

  • соответствующих обозначениям (ключам) в файле /config/js-css-meta.php, как они обозначаются при подключении через View (это удобнее тем, что при изменении местоположения файла, потребуется изменить его путь только в настроечном файле); если обозначения js и css файлов совпадают, то будут подгружены оба файла, например: 'date_picker', будут подгружены js файл и css файл данного плагина;
  • указать пути до js, css файлов.

AjDi::IMPORT => [
    'flot',
    'flot_pie',
    'date_picker'
]

AjDi::IMPORT => [
    '/js/flot/jquery.flot.min.js',
    '/js/flot/jquery.flot.pie.min.js',
    '/css/jquery.datetimepicker.css',
    '/js/jquery.datetimepicker.full.min.js'
]

return AjDi::getReply(
    [
        '#some' => 'some content',
        AjDi::INFOBLOCK => [
            'limit' => 'any',
            'name' => 'some-name',
            'content' => 'infoblock content inc a tag and ul'
        ],
        AjDi::IMPORT => [
            'flot',
            'flot_pie'
        ]
    ]
);

return AjDi::getReply(
    [
        '#some' => 'some content',
        AjDi::INFOBLOCK => [
            [
                'limit' => 'any',
                'content' => 'infoblock content inc a tag and ul'
            ],
            [
                'content' => 'infoblock content inc a tag and ul'
            ]
        ]
    ]
);

return AjDi::getReply([
    '#some' => 'some content',
    AjDi::INFOBLOCK => 'infoblock content inc a tag and ul'
]);

Например, создание диспетчера:

Инициализируем View. См. View.
Передаем в параметр 'content' макета View::BASIC_LAYOUT параметр View::WORKFLOW (соответствует основной части страницы, где может располагаться большая таблица) с ajDi, который будет обращаться по url http://domain/incoming/ajax-data и передаст туда параметры.


use xtables\base\View;
use xtables\base\AjDi;

private function layoutForTableView() {
    $view = new View([
        'head' => [
            'css' => [
                'date_picker'
            ],
            'js' => [
                'date_picker',
                'autocomplete'
            ]
        ],
        'meta' => [
            'title' => 'Журнал',
            'description' => 'some description'
        ],
        'layout_name' => 'xtables-basic'
    ]);
    return $view->setContent([
        View::PAGE_TITLE => 'Журнал',
        View::WORKFLOW => AjDi::getJs('/incoming/ajax-data', $this->params, 300)
    ])->get();
}
    

Создание ответа для ajDi в модели, которая будет вызвана методом контроллера:


use xtables\components\TableView\TableView;
use xtables\base\AjDi;

protected function getByAjax() {
    $this->params = array_merge($this->params, include 'table.php');
    $this->table = new TableView($this->params);
    return AjDi::getReply([
        View::WORKFLOW => $this->table->get(), // название селектора css, куда вставлять данные ответа
        View::WORKFLOW_SEARCH => $this->table->getSearchForm()
    ]);
}
    

JSONP

Также можно использовать ajDi для кросс доменных запросов (запросов между разными доменами).
Однако таким образом можно передать только данные в формате json, что является ограничением технологии в целях безопасности.

Для использования jsonp нужно передать в метод AjDi::getJs в качестве url адрес, начинающийся http или //.

В методе контроллера, который будет возвращать ответ (будет расположен на другом домене) нужно использовать метод класса xtables\base\Response - jsonp($json), в который передать данные в формате json.

Пример кросс доменных запроса и ответа:

Запрос с домена site-request.ru


use xtables\base\AjDi;

AjDi::getJs('http://site-reply.ru/route/to', $this->params, 300);
//AjDi::getJs('//site-reply.ru/route/to', $this->params, 300); // for use with http and https

Ответ с домена site-reply.ru


use xtables\xTables;
use xtables\base\AjDi;

$reply = AjDi::getReply([
    '#some-css-id' => 'any data, html data or other to transfer'
]);

xtables::$response->jsonp($reply);

JS функции

На клиенте могут быть использованы функции:

xtables.startAjDi(params)

В фукнцию xtables.startAjDi() нужно передать агрументы:

  • параметры в json:
    • обязательно
      string url - адрес на том же или другом домене;
      @since version 1.7.22
      string|function target - для получения данных локально, можно вместо url использовать данный параметр (он имеет приоритет, если указан), в котором указать либо наименование функции либо анонимную функцию, в этом случае произойдет обращение в данную функцию, ей будут переданы параметры; функция должна возвратить ответ в виде JS объекта либо строки и он будет обработан также, как будто это ответ от сервера;
    • json data - необязательно данные, которые будет переданы на сервер;
    • int refresh - необязательно время в секундах, через которое должна происходить перезагрузка аякс конетейнера;
    • array|string reloadBundles - необязательно массив с именами селекторов, аякс контейнеры которых должны быть перезагружены (reload без новых параметров) после выполнения запроса (обычный массив, не json), или строка с именами селекторов через запятую;
    • string history - необязательно url адрес для помещения его в историю браузера для обеспечения ajax навигации;
    • function|string before - необязательно функция или строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
      в данную функцию будет передан обернутый в jQuery this (должен быть передан в качестве второго аргумента в xtables.startAjDi()); функция будет выполнена первой до всех остальных действий, соответственно функция может, например, изменить значение каких-то элементов;
      если функция вернет false, то дальнейшее выполнение будет остановлено;
    • function|string after - необязательно функция или строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
      в данную функцию будет передана обернутый в jQuery this (должен быть передан в качестве второго аргумента в xtables.startAjDi()) и data, которые будут получены в качестве ответа от сервера; по умолчанию полученные данные обрабатываются функцией xtables.serverReplyHandle, если функция вернет false, то данная функция не будет вызвана, это может быть использовано для переопределения функционала ответа;
  • $this - обернутый в jQuery this

// request to server
xtables.startAjDi({
    before: function() {

    },
    after: function() {

    },
    url: '/some/url',
    data: {
        some: 'some data',
        else: 1
    },
    refresh: 0,
    reloadBundles: ['#selector1', '#selector2'],
    history: '/some/new/url'
}, jQuery(this));

// local request to function
xtables.startAjDi({
    before: function() {

    },
    after: function() {

    },
    target: function(data) {
        // some function to handle data and
        // return reply similar to server ajDi reply (object or string)
    },
    // target: 'xtables.someFunc',
    // target: 'window.someFunc',
    // target: 'someFunc',
    data: {
        some: 'some data',
        else: 1
    },
    refresh: 0,
    reloadBundles: ['#selector1', '#selector2'],
    //history: '/some/new/url'
}, jQuery(this));

xtables.ajDiBundle(selector, url, data)

Эта функция используется для перезагрузки аякс контейнера по имени селектора. Если в фукнцию будут переданы url, data, то произойдет обновление данных ajax dispatcher. Если будет только сообщен селектор, то произойдет простая перезагрузка.

  • string selector - обязательно наименование селектора по правилам CSS (#, .);
  • string url - необязательно адрес;
  • json data - необязательно данные, которые будет переданы на сервер.

xtables.ajDiBundle('#someId');
// or
xtables.ajDiBundle('#someId', '/some/url', {
    some: 'some data',
    else: 1
});

xtables.reloadAjDi(params)

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

  • string num - обязательно имя аякс диспетчера;
  • string url - необязательно адрес на том же или другом домене для изменения соответствующего параметра в ранее загруженном аякс диспетчере, для простой перезагрузки вводить адрес не нужно;
  • json data - необязательно данные, которые заменят передающиеся данных аякс диспетчером данные, для простой перезагрузки вводить этот параметр не нужно;
  • string history - необязательно url адрес для помещения его в историю браузера для обеспечения ajax навигации

xtables.reloadAjDi({num: 'ajDi234'});

// OR

xtables.reloadAjDi({
    num: 'ajDi234', // some ajDi name
    url: '/some/url',
    data: {
        some: 'some data',
        else: 1
    },
    history: '/some/new/url'
});

Использование ajax dispatcher на ссылках и кнопках

Использование ajax dispatcher может быть осуществлено через data атрибуты ссылки или кнопки. Для этого достаточно добавить к ним css класс ajax. В качестве параметров будут использоваться:

  • обязательно href или data-url - адрес для направления аякс запроса, параметры можно добавить в ссылку; при этом запрос будет отправлен методом POST и к нему будет добавлен csrf токен;
    @since version 1.7.22
    data-target - для получения данных локально, можно вместо url использовать данный параметр (он имеет приоритет, если указан), в котором указать наименование функции, в этом случае произойдет обращение в данную функцию, ей будут переданы параметры (данные из дата параметров); функция должна возвратить ответ в виде JS объекта либо строки и он будет обработан также, как будто это ответ от сервера;
  • data-before - необязательно строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
    в данную функцию будет передана обернутый в jQuery this ссылки; функция будет выполнена первой до всех остальных действий, соответственно функция может, например, изменить значение каких-то элементов;
    если функция вернет false, то переход по ссылке не будет осуществлен, ссылка будет заблокирована, пока функция не перестанет возвращать false;
  • data-after - необязательно строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
    в данную функцию будет передана обернутый в jQuery this ссылки и data, которые будут получены в качестве ответа от сервера; по умолчанию полученные данные обрабатываются функцией xtables.serverReplyHandle, если функция вернет false, то данная функция не будет вызвана, это может быть использовано для переопределения функционала ответа;
  • data-refresh - необязательно время для периодического обновления аякс контейнера в секундах;
  • data-history - необязательно для обеспечения аякс навигации нужно использовать данный параметр, в который передать адрес для изменения истории браузера; если в качестве значения указать url или href, то будет использован адрес, который соответствует url аякс запроса
  • data-bundles - необязательно список селекторов через запятую, аякс конейтнеры которых должны быть перезагружены после выполнения запроса;
  • data-... - все остальные дата параметры будут переданы по роуту или в функцию локально, если выбран data-target.

<!-- to server -->
<a class="ajax" href="/some/url?id=1" data-history="url" data-refresh="180"
        data-bundles="#selector, #selector2" data-before="xtables.someFunc">Link</a>
<button class="ajax" data-url="/some/url?id=1" data-history="url" data-refresh="180"
        data-bundles="#selector, #selector2" data-before="xtables.someFunc">Button</button>
<!-- local to function -->
<a class="ajax" href="#" data-target="someFunc" data-refresh="180"
        data-bundles="#selector, #selector2" data-before="xtables.someFunc">Link</a>

Использование ajax dispatcher на формах

Использование ajax dispatcher на html формах может быть осуществлено через data атрибуты формы. Для этого достаточно добавить к ним css класс ajax-form.

@since version 1.7.22
если необходимо, чтобы форма была передана локально в функцию, то вместо url нужно использовать параметр data-target (он имеет приоритет, если указан), в котором указать наименование функции, в этом случае произойдет обращение в данную функцию, ей будут переданы параметры (данные из дата параметров и поля формы (input, textarea, checkbox, radio, select)); функция должна возвратить ответ в виде JS объекта либо строки и он будет обработан также, как будто это ответ от сервера.
Таким образом, поведение не изменится лишь за тем исключением, что все данные будут направлены не на сервер по роуту, а в функцию.

Также в функцию, определенную в data-target, в параметре $this будет передан обернутый в jQuery this, соответствующий форме (тегу form).

Имеется возможность шифрования передаваемых на сервер в полях типов input, textarea значений, используя открытый ключ RSA. Использование шифрования в ajax формах происходит максимально просто и незаметно. Достаточно добавить к нужному полю input, textarea css класс crypt. При отправке содержимое поля будет заменяться на ***CRYPT***.

Длина передаваемых в шифруемом поле данных не должна превышать 465 символов.

Для формирования полей, обязательных к заполнению, в input, textarea таких аякс форм можно добавить класс .required.


<-- to server -->
<form action="/some/url" class="ajax-form">
    <div class="form-group">
        <input type="text" name="some_name" class="form-control required"> <!--prevent to submit if empty-->
    </div>
    <div class="form-group">
        <input type="text" name="else_name" class="form-control crypt"> <!--will be crypted-->
    </div>
    <div class="form-group">
        <button class="btn btn-primary" type="submit">
            Send
        </button>
    </div>
</form>

<-- local to function -->
<form data-target="someFunc" data-id="243" class="ajax-form">
    <div class="form-group">
        <input type="text" name="some_name" class="form-control required"> <!--prevent to submit if empty-->
    </div>
    <div class="form-group">
        <input type="text" name="else_name" class="form-control crypt"> <!--will be crypted-->
    </div>
    <div class="form-group">
        <button class="btn btn-primary" type="submit">
            Send
        </button>
    </div>
</form>

Для обозначения шифруемых полей input можно воспользоваться фукнционалом, предоставляемым css фреймворком Bootstrap.

RSA

В качестве параметров могут использоваться:

  • data-before - необязательно строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
    в данную функцию будет передана обернутый в jQuery this формы; функция будет выполнена первой до всех остальных действий, соответственно функция может, например, изменить значение каких-то элементов;
    если функция вернет false, то форма не будет отправлена;
  • data-after - необязательно строка с именем функции, может содержать название объекта через точку, из строки будет извлечена ссылка на объект и функцию;
    в данную функцию будет передана обернутый в jQuery this формы и data, которые будут получены в качестве ответа от сервера; по умолчанию полученные данные обрабатываются функцией xtables.serverReplyHandle, если функция вернет false, то данная функция не будет вызвана, это может быть использовано для переопределения функционала ответа;
  • action - обязательно адрес для направления аякс запроса; при этом запрос будет отправлен методом POST и к нему будет добавлен csrf токен;
  • data-timer - необязательно после отправки запроса кнопка (инпут submit) блокируются, для показа таймера на ней можно воспользоваться данным параметром, определив его со значением 1;
  • data-refresh - необязательно время для периодического обновления аякс контейнера в секундах;
  • data-history - необязательно для обеспечения аякс навигации нужно использовать данный параметр, в который передать адрес для изменения истории браузера; если в качестве значения указать url или href, то будет использован адрес, который соответствует url аякс запроса
  • data-bundles - необязательно список селекторов через запятую, аякс конейтнеры которых должны быть перезагружены после выполнения запроса;

<form class="ajax-form" action="/some/url" data-timer="1" data-before="xtables.someFunc">
    <input type="text" name="some_name">
    <input type="text" name="some_name2">
    <input type="text" name="some_name3" class="crypt"> <!--{will be crypted}-->
    <button>
        Submit this form
    </button>
</form>

Поведение ключевых компонентов

При использовании ajDi поведение ключевых компонентов xTables: TableView, CardView, CardNew, изменяется.

В TableView пагинация и сортировка начинают обрабатываться через ajax запросы с загрузкой в те же элементы DOM тем же ajDi, ссылки в таблице, которым присвоен css класс 'ajax' будут вызывать ajax dispatcher по указанному в них адресу.

Если компонент загружен без использования ajDi, то все ссылки сохрянят свое обычное поведение и будут обеспечивать переход по указанному в них адресу. ajDi будет загружать содержимое в те селекторы, которые будут сообщены в массиве, аналогичном передаваемому при создании первоначального диспетчера (правильный массив может быть создан с использованием метода xtables\base\AjDi::getReply()).

В CardView при использовании ajDi при редактировании карточки сохранение будет происходить с использованием того же диспетчера, что и первоначально загружал компонент и соответственно в том же элементе DOM.

В CardNew при использовании ajDi при сохранении новой записи, созданная карточка будет открыта с использованием того же диспетчера, что и первоначально загружал компонент и соответственно в том же элементе DOM.


Карточка
Настройки

Настройки осуществляются путем передачи параметров в методы, описано выше.