AJAX и сниппеты
В эпоху современных веб-приложений, когда функциональность часто разрывается между сервером и браузером, AJAX является необходимым связующим элементом. Какие возможности предлагает Nette Framework в этой области?
- передача частей шаблона, так называемых сниппетов
- передача переменных между PHP и JavaScript
- инструменты для отладки AJAX-запросов
Запрос AJAX
AJAX-запрос принципиально не отличается от классического HTTP-запроса. Вызывается презентатор с определенными параметрами. Как ответить на запрос, зависит от ведущего – он может вернуть данные в формате JSON, отправить часть HTML-кода, XML-документ и т.д.
На стороне браузера мы инициируем AJAX-запрос с помощью функции
fetch()
:
На стороне сервера AJAX-запрос распознается методом
$httpRequest->isAjax()
сервиса, инкапсулирующего
HTTP-запрос. Он использует HTTP-заголовок X-Requested-With
, поэтому его
необходимо отправить. Внутри презентатора можно использовать метод
$this->isAjax()
.
Если необходимо отправить данные в формате JSON, используйте метод sendJson()
метод. Метод также завершает
работу презентера.
Если вы планируете отвечать с помощью специального шаблона, предназначенного для AJAX, то это можно сделать следующим образом:
Фрагменты
Наиболее мощным инструментом, предлагаемым Nette для связи сервера с клиентом, являются сниппеты. С их помощью можно превратить обычное приложение в AJAX-приложение, затратив минимум усилий и написав несколько строк кода. Пример Fifteen демонстрирует, как это работает, а его код можно найти на GitHub.
Сниппеты, или вырезки, позволяют обновлять только отдельные части страницы, а не перезагружать ее целиком. Это быстрее и эффективнее, а также обеспечивает более комфортную работу пользователя. Возможно, сниппеты напомнят вам Hotwire для Ruby on Rails или Symfony UX Turbo. Интересно, что компания Nette представила сниппеты на 14 лет раньше.
Как работают сниппеты? При первой загрузке страницы (не-AJAX-запрос) загружается вся страница, включая все сниппеты. Когда пользователь взаимодействует со страницей (например, нажимает кнопку, отправляет форму и т.д.), вместо загрузки всей страницы выполняется AJAX-запрос. Код в презентере выполняет это действие и решает, какие фрагменты необходимо обновить. Nette рендерит эти фрагменты и отправляет их в виде массива JSON. Затем код обработки в браузере вставляет полученные фрагменты обратно на страницу. Таким образом, передается только код измененных фрагментов, что позволяет экономить пропускную способность и ускорять загрузку по сравнению с передачей всего содержимого страницы.
Naja
Для работы со сниппетами на стороне браузера используется библиотека Naja. Установите ее как пакет node.js (для использования с такими приложениями, как Webpack, Rollup, Vite, Parcel и другими):
… или вставьте ее непосредственно в шаблон страницы:
Сначала нужно инициализировать библиотеку:
Чтобы превратить обычную ссылку (сигнал) или отправку формы в
AJAX-запрос, достаточно пометить соответствующую ссылку, форму или
кнопку классом ajax
:
Перерисовка фрагментов
Каждый объект класса Control (в том числе и сам Presenter)
хранит информацию о том, произошли ли изменения, требующие его
перерисовки. Для этого используется метод redrawControl()
.
Nette также позволяет более тонко контролировать, что именно нужно перерисовывать. Упомянутый выше метод может принимать в качестве аргумента имя фрагмента. Таким образом, можно аннулировать (то есть принудительно перерисовать) на уровне части шаблона. Если аннулируется весь компонент, то перерисовывается и каждый его фрагмент:
Фрагменты в Latte
Использовать сниппеты в Latte очень просто. Чтобы определить часть
шаблона как сниппет, достаточно обернуть ее в теги {snippet}
и
{/snippet}
:
Сниппет создает элемент <div>
в HTML-странице со специально
сгенерированным id
. При перерисовке сниппета содержимое этого
элемента обновляется. Поэтому при первоначальном рендеринге страницы
все сниппеты также должны быть рендерированы, даже если они изначально
могут быть пустыми.
Также можно создать сниппет с элементом, отличным от <div>
с
помощью атрибута n:attribute:
Области фрагментов
Имена сниппетов также могут быть выражениями:
Таким образом, мы получим несколько фрагментов типа item-0
,
item-1
, и т.д. Если мы напрямую аннулируем динамический сниппет
(например, item-1
), то ничего не будет перерисовано. Причина в том,
что сниппеты работают как настоящие выдержки, и непосредственно
отрисовываются только они сами. Однако в шаблоне технически нет
фрагмента с именем item-1
. Он появляется только при выполнении
окружающего сниппета кода, в данном случае цикла foreach. Поэтому часть
шаблона, которую необходимо выполнить, мы пометим тегом
{snippetArea}
:
И перерисуем как отдельный фрагмент, так и всю общую область:
Также необходимо убедиться, что массив $items
содержит только те
элементы, которые должны быть перерисованы.
При вставке другого шаблона в основной с помощью тега {include}
,
содержащего сниппеты, необходимо снова обернуть включаемый шаблон в
snippetArea
и сделать недействительными и сниппет, и область
вместе:
Сниппеты в компонентах
Вы можете создавать сниппеты внутри компонентов, и
Nette будет автоматически перерисовывать их. Однако есть одно
ограничение: для перерисовки сниппетов вызывается метод render()
без параметров. Таким образом, передача параметров в шаблоне не
работает:
Отправка пользовательских данных
Наряду со сниппетами можно отправлять клиенту любые дополнительные
данные. Для этого достаточно записать их в объект payload
:
Параметры отправки
Когда мы отправляем в компонент через AJAX-запрос параметры, будь то
сигнальные или постоянные, мы должны указать их глобальное имя,
которое также содержит имя компонента. Полное имя параметра
возвращает метод getParameterId()
.
handle метод с соответствующими параметрами в компоненте: