Lab / Laravel

Laravel: Работа с AJAX запросами на чистом Javascript без jQuery зависимости.

Сегодня мы поговорим о том как при создании сайтов делать запросы AJAX, получать ответы и при этом не будем использовать лишние обвесы в виде jQuery. Будем писать всё на чистом, лаконичном JavaScript.

Вы наверное часто замечали что на сайтах часто встречаются формы или кнопки которые принимают от вас какие-либо данные а затем их отправляют или как либо меняют часть интерфейса без полной перезагрузки страницы и без потери текущего состояния. Всё это реализовано благодаря технологии, известной как AJAX или http запрос (XMLHttpRequest).

Как это работает? На стороне клиента (в браузере) вы заполняете какую-либо форму или взаимодействуете с каким-либо контролом, JavaScript ловит это событие, принимает некие данные и отправляет их на определённый контроллер (по просту страницу). Далее этот контроллер принимает данные и даёт какой-либо ответ, который мы в последствии можем получить в калбеке.

Итак давайте же наконец начнём кодить. Как я уже говорил ранее все запросы мы будем писать на чистом JavaScript. Для начала давайте создадим событие. В первом варианте это будет загрузка страницы, а во втором изменение значения <select> тега.

Итак событие №1 загрузка страницы:

window.onload = function () { // Событие страница загружена
 sendRequest(); // Вызываем функцию с ajax запросом
}

В коде из данного примера мы повесили событие на загрузку страницы. Теперь давайте напишем функцию которая отрабатывает по этому событию, как вы наверное уже догадались, функция будет называтся sendRequest()

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

function sendRequest(){
 var request = new XMLHttpRequest();
}

Что же такое var request = new XMLHttpRequest()? XMLHttpRequest() это JS класс отвечающий за асинхронные запросы в браузере именно через него мы будем отправлять и получать данные.

Так давайте же наконец отправим наши данные на специальную страницу:

function sendRequest(){
 var request = new XMLHttpRequest();

 request.open('GET', '/request');
 request.send();
}

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

function sendRequest(){
 var request = new XMLHttpRequest(); // Создвём объект запроса

 request.open('GET', '/request'); // Указываем куда отправить запрос
 request.send(); // Выполняем отправку 

 request.onreadystatechange = function () { // Дожидаемся ответа
 if (request.readyState == 4 && request.status == 200){// Делаем проверку если ответ получен и страница отдала код 200 (открылась)
 var response = request.responseText; // Получаем ответ как текст (включая html) и сохраням в переменную
 console.log(response); // Выводим данные в консоль
 }
 }
}

Отлично, вы отправили ваш первый запрос, убедиться в этом можно в инспекторе вашего браузера. В коде выше мы получили ответ от сервера в методе onreadystatechange и использовали его как событие. Далее выполнили проверку того, что ответ получен полностью методом readyState который принимает значение равное 4 при получении ответа от сервера. Затем мы добавили дополнительную проверку на код ответа сервера, в случае если он равен 200 означает что запрашиваемый ресурс найден и ответил корректно. Контр пример это код 404 когда страница не найдена или 500 когда произошел сбой на стороне сервера.

Собственно для окончания теоретической части вам лишь отсталось создать роут request и контроллер который будет передавать туда, какие-либо данные, напрмер список задач. Кстати если в контроллере на выходе указать массив объектов типа return $tasks Laravel сам преобразует его в JSON коллекции и отдаст в таком виде.

Вариант с выпадающим списком

В данном прмере я очень кратко покажу как можно при помощи AJAX сделать зависимый выпадающий список на Laravel. Для начала нам понадобится создать форму. Для этого я использую конструктор форм от Laravel Collective. Итак ближе к делу, после того как вы освоитесь с постоением форм через выше упомянутый конструктор, можно приступать к практике, вот моя простенькая интерпретация задачи:

{!! Form::open(['route' => ['node.store'], 'files' => true]) !!}
...
{{ Form::select('type', ['places' => 'Места', 'events' => 'События'], $node->type, ['placeholder' => 'Тип материала', 'onchange' => 'getCategory(this)']) }}
{{ Form::select('category', ['1' => 'Упс!!! Данные не пришли', '0' => ''], $node->category, ['placeholder' => 'Категория', 'id' => 'categories']) }} 
...
{!! Form::close() !!}

Принцип здесь очень простой, предположим у нас есть два типа материала: места и события, у каждого из типов разнятся категории, но во всём остальном они в целом схожи, соответственно разделять их нет смысла, категории то и AJAXом можно подцепить =) Так вот в поле type у нас как раз эти типы и описанны. Осталось лишь перехватить событие выбора типа материала и закинуть во второй селектор категории относящиеяс к выбранному типу. Для этого мы повесили на поле событие onchange и передали элемент с выбранным вариантом через переменную this.

Теперь давайте создадим функцию которую вызывает событие и передадим туда полученный ответ:

 // Ajax category
 function getCategory(type){ // вызывнная функция, принимает данные в переменную type
 var request = new XMLHttpRequest(); // как в примере выще готовим запрос
 var params = "type="+type.value; // Выбираем из элемента выбранное пользователем значение и записываем в переменую params

 request.onreadystatechange = function () {
 if (request.readyState == 4 && request.status == 200){
 var categoryList = request.responseText; // тут всё тоже самое, получаем ответ
 document.getElementById("categories").innerHTML = categoryList; // и вуаля, запихиваем его во второй селектор
 }
 }

 request.open('GET', '/get-category?'+params, true); отправляем запрос + параметр с типом выбранного материала
 request.send();
 }

Вот и вся магия! Осталось только в контроллере получить данные создать проверку на тип и в соответствии с этим отдать список необходимых категорий:

 public function getCategory(Request $request)
 {
 if ($request->type == 'places'){
 $cat = new \App\Category;
 $terms = $cat->placeCategory();

 foreach ($terms as $key => $item) {
 echo "<option value='$key'>" .$item . "</option>";
 }
 }
 if ($request->type == 'events'){
 $cat = new \App\Category;
 $terms = $cat->eventCategory();

 foreach ($terms as $key => $item) {
 echo "<option value='$key'>" .$item . "</option>";
 }
 }

 }

У меня есть небольшая хитрость в контроллере, я вызываю не известный вам метод placeCategory() и eventCategory(), вам же не обязательно так всё усложнять, и можно сократить всё до банального:

$terms = App\Category::pluck('title','id')->all()

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

 public function eventCategory()
 {
 $terms = $this::where('taxonomy', 'gd_eventcategory')->where('parent', 0)->get();
 $items = [];
 foreach ($terms as $item) {
 $items[$item->term_id] = $item->name($item->term_id);

 $subs = $this::where('parent', $item->term_id)->get();
 foreach ($subs as $sub) {
 $items[$sub->term_id] = "&emsp; " . $sub->name($sub->term_id);
 }

 }
 return $items;
 }