Марафон. Фронтенд. Создание jQuery плагина для викторины.

Формулировка задачи.

Взять готовый шаблон фронтенд приложения.

start page

ссылка на репозиторий с шаблоном

Используя REST API бекенд, расположенный по адресу http://quiz.webmonstr.com/

Админка http://quiz.webmonstr.com/admin

Логин - admin

Пароль - admin

Создать приложение, используя библиотку jQuery в виде плагина.

Подключение плагина на странице.

  <script src="app.js"></script>
  <script>
      var app = $('#rootBlock').quizPlugin();
      app.start();
  </script>

Суть работы приложения:

В начале проверяется переменная username в localStorage браузера.

Если этой переменной нет, то пользователю выводится форма авторизации со списком стикеров и полем, где он указываем свое имя.

Стикеры запрашиваются по адресу http://quiz.webmonstr.com/v1/quiz/sticker/list

После ввода имени и выбора стикера послать на адрес http://quiz.webmonstr.com/v1/quiz/player/join POST запрос с данными json в формате:

{
  "name": "string",
  "sticker_id": 0
}

Проанализировать ответ от сервера.

В случае успешного ответа (отсутсвие в нем поля error):

  1. Cохранить имя пользователя в localStorage.

  2. Убрать форму авторизации.

  3. Вывести чат-комнату.

В случае ошибки вывести ее на странице над кнопкой формы красным цветом.

Вывод чат-комнаты.

  1. Запросить текущих игроков и вывести в блоке #playerListBlock согласно дизайну.

Выводим стикер и имя пользователя.

Вместо слова Admin выводит текущий счет пользователя.

Запросить список игроков можно по адресу:

http://quiz.webmonstr.com/v1/quiz/player/list
  1. Запросить текущий вопрос викторины и вывести в блоке #questionBlock

Адрес запроса:

http://quiz.webmonstr.com/v1/quiz/get_current_question

Формат ответа.

{
  "lang": "ru",
  "level": 3,
  "tp": "questionend",
  "mode": "fullmatch",
  "theme": 4,
  "question": "Какое слово образовано от начальных слов песни «Прелестная Катарина»?",
  "answers": "ШАРМАНКА",
  "is_published": true,
  "order": 1
}
  1. Запросить список ответов и вывести в блоке chatMessageBlock согласно дизайну.

Адрес запроса.

Формат ответа.

Ответы, помеченные как неправильные, выводить на розовом фоне, правильные - на салатовом.

  1. При вводе ответа и нажатии на кнопку Send или клавиши Enter отправлять POST сообщение на сервер.

Ответ отправлять только в случае не пустого сообщения.

Адрес.

http://localhost:7777/v1/quiz/save_message

Формат отправляемых данных:

{
  "message": "string",
  "playername": "string"
}

Ответ игнорировать.

  1. Создать веб-сокет соединение с сервером.

Адрес веб-сокет соединения.

ws://localhost:7777/quiz/

Обеспечить реакцию приложения на следующие сообщения.

Сообщение о новом сообщении.

Формат сообщения:

{ 
    'type': 'quiz_message', 
    'message': {...}
}

При приходе сообщения:

  1. Вывести его в списке сообщений согласно дизайну и логике описанной выше.

  2. В случае если сообщение помечено как правильное,

    2.1 Обновить список игроков (либо использовать сообщение об обновлении аккаунта см. ниже) и проиграть произвольный звук.

    2.2 Запросить следующий вопрос и обновить блок #questionBlock

Сообщение о удалении сообщения.

Формат.

{ 
    'type': 'quiz_delete_message', 
    'message': {...}
}

При поступлении сообщения удалять его из списка по ID в сообщении.

Сообщение об обновлении аккаунта.

{ 
    'type': 'quiz_update_account', 
    'message': {...}
}

При поступлении сообщения обновить аккаунт пользователя по его ID.

Фронтенд разработка на языке JavaScript. -> Задание для викторины.

Задание викторины.

Создать фронтенд приложение программы “Попутчик” используя библиотеку jQuery.

ссылка на бекенд

Бекенд отрадает все возможные запросы на сервер, форматы запросов и ответов.

Суть приложения.

Пользователю доступно 4 раздела.

  1. Форма заказа.

  2. Поиск заказов.

  3. Личный кабинет.

  4. Регистрация.

Регистрация.

При регистрации пользователь вводит свое имя и выбирает город.

Форма заказа.

Пользователь заполняет следующие поля.

  1. Начальная точка поездки.

  2. Конечная точка поездки.

  3. Дата и время поездки.

Поиск заказов.

В данном разделе пользователь может провести поиск интересующих заказов по выбранному раену города.

Далее пользователь может присоединиться к поездке.

Личный кабинет.

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

По каждому заказу он может отправить сообщение всем, кто к нему присоединен.

Технические требования.

Дизайн взять на основе bootstrap и сориентировать на мобильную версию клиента.

Фронтенд разработка на языке JavaScript. -> Создание jQuery плагина для викторины.

Фронтенд. Чат-викторина. JQuery

Установка готового шаблона.

git clone git@github.com:zdimon/marafon-js-quiz-template.git

Скопируем его в папку public проекта.

Сервер разработки.

Поставим простенький сервер.

npm install lite-server

Запуск.

cd public
../node_modules/lite-server/bin/lite-server

Работать будем внутри файла app.js.

Пишем плагин.

Нам вначале необходимо создать изолированную область видимости переменных.

(function( $ ) {
    $.fn.quizPlugin = function() {

        console.log('my plugin');

    };
})(jQuery);

В jQuery fn - это псевдоним прототипа.

Ключевое слово jQuery (или $) - это всего лишь функция-конструктор, и все объекты, создаваемые этим конструктором будут наследовать все свойства и методы его прототипа.

$.fn.quizPlugin = function() {..}

Так мы создаём новое свойство-функцию для объекта jQuery, где именем нового свойства будет имя нашего плагина.

Для того, чтобы небыло конфликтов со знаком $ рекомендуется «обернуть» объект jQuery в непосредственно выполняемую функцию-выражение.

Применяем плагин.

<script>
      var app = $('#myapp').quizPlugin();
</script>

Проверяем залогиненность.

    app.start = function() {

        if(sessionStorage.getItem('username')) { 
            this.initRoom();
        } else {
            this.loginForm()
        }
    }

    app.loginForm = function() {

    }

    app.initRoom = function() {

    }

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

Функция вывода формы логина с запросом и вставкой стикеров.

    app.loginForm = function() {
        let form = $('#loginForm').show();
        let url = 'http://quizapi.webmonstr.com/v1/quiz/sticker/list';
        $.get( url, function( data ) {
            data.forEach((el) => {
                let img_tag = `<img width="50" src="${el.get_url}" />`;
                let parent = $('#stickers').append(img_tag);
             })
          });
    }

$.get - функция jQuery отсылающая http запрос.

data.forEach((el) => {}) - функция, перебирающая список.

$(‘#stickers’).append() - добавление html в выбранный элемент.

Привязываем колбек к клику на стикер, убирая класс активности.

 let img_tag = `<img data-id="${el.id}" class="sticker" width="50" src="${el.get_url}" />`;
...
 $('.sticker').on('click',(event)=>{
     $('.sticker').each((indx,el)=>{
         $(el).removeClass('active-sticker');
     })
     $(event.target).addClass('active-sticker');
 })

start page

$(‘.sticker’).on(‘click’,(event)=>{}) - привязка событий к коллбеку в jQuery.

Тут мы набрасывае класс sticker на элемент img чтобы далее по ним пройтись циклом и убрать класс active-sticker. Далее мы устанавливаем класс active-sticker на том изображении, на которое кликнул пользователь.

Стилизируем класс.

.active-sticker {
    border: 1px solid red;
}

Сабмитим форму логина.

Создадим функцию отправки данных методом post с передачей json в теле запроса.

    app.submitLogin = function() {
        let data = {
            name: $('#userName').val(),
            sticker_id: this.sticker
        };
        let url = 'http://quizapi.webmonstr.com/v1/quiz/player/join';
        $.post( url, data, ( response ) => {
            sessionStorage.setItem('username',response.name);
            let form = $('#loginForm').hide();
        });
    }

$(‘#userName’).val() - получение данных, вводимых пользователем в элемент input.

$.post( url, data, ( response ) => {}) - передача данных POST в jQuery.

Привяжем функцию к нажатию кнопки с id = “chat-start”.

    app.loginForm = function() {
        ...
          $('#chat-start').on('click',() => {this.submitLogin()});
    }

Получение и отображение текущего вопроса.

    app.getCurrentQuestion = function() {
        let url = 'http://quizapi.webmonstr.com/v1/quiz/get_current_question';
        $.get( url, ( response ) => {
            $('#currentQuestionBlock').html(response.question);
        });

    }

$(‘…’).html(content) - забрасываение html внутрь полученного элемента.

Отображение списка ответов.

    app.getMessages = function() {
        let url = 'http://quizapi.webmonstr.com/v1/quiz/message/list';
        $.get( url, ( response ) => {
            response.forEach((el)=> {
                let tpl = `                                             <div class="chat">
                <div class="chat-user">
                   <a class="avatar m-0">
                   <img src="${el.playerimage}" alt="avatar" class="avatar-35 ">
                   </a>
                   <span class="chat-time mt-1">${el.playername}</span>
                </div>
                <div class="chat-detail">
                   <div class="chat-message">
                      <p>${el.text}</p>
                   </div>
                </div>
             </div>`;
             $('#chatContent').append(tpl);
            })
        });
    };

Веб-сокет соединение.

    ...

    app.socketConnection = function() {
        let webSocket = new WebSocket('ws://quizapi.webmonstr.com:7777/quiz/');

        webSocket.onerror = (evt) => {

        }

        webSocket.onmessage = (event) => {
            var payload = JSON.parse(event.data)
            console.log(payload);
        }

        webSocket.onclose =  (event) => {
            console.log('Close connection');
        };

        webSocket.onopen =  (event) => {
            console.log('Connection established');
        };
    }

При создании объекта соединения нам необходимо определить несколько обработчиков.

onerror - при ошибке

onmessage - приход нового сообщения

onopen, onclose - открытие и закрытие соединения.

Отправка ответа на сервер.

    app.sendMessage = function() {
        let data = {
            message: $('#messageBox').val(),
            playername: sessionStorage.getItem('username')
        }
        let url = 'http://quizapi.webmonstr.com/v1/quiz/save_message';
        $.post( url, data, ( response ) => {
            $('#messageBox').val('');
        });
    };

$(‘#messageBox’).val(‘’) - очищение input-а.

Реагируем на приход сообщения по веб-сокету.

    ....

    webSocket.onmessage = (event) => {
        var payload = JSON.parse(event.data)
        if(payload.type === 'message'){
            this.getMessages();
        }
    }

    ...

Отображаем список игроков.

    ...
    app.getUserList = function() {
        $('#playerListBlock').empty();
        let url = 'http://quizapi.webmonstr.com/v1/quiz/player/list';
        $.get( url, ( response ) => {
            response.forEach((el) => {
                let tpl = `<div class="media-height p-3">
                <div class="media align-items-center mb-4">
                   <div class="iq-profile-avatar status-online">
                      <img class="rounded-circle avatar-50" src="${el.sticker.get_url}" alt="">
                   </div>
                   <div class="media-body ml-3">
                      <h6 class="mb-0"><a href="#">${el.name}</a></h6>
                      <p class="mb-0">${el.account}</p>
                   </div>
                </div>
             </div>`;
                $('#playerListBlock').append(tpl);
            });
        });
    };

Полная версия инициализации комнаты.

    app.initRoom = function() {
        this.setCurrentUser();
        this.getUserList();
        this.getCurrentQuestion();
        this.getMessages();
        this.socketConnection();
        $('#sendButton').on('click',()=>{
            this.sendMessage();
        });
    }