Построение JavaScript приложения без фреймворка.

Задача: построить простое JavaScrpt приложения без фреймворков (BackboneJS, AngularJS).
Будем использовать jQuery и Undescore.
При этим код не должен превратиться в «лапшу».

Наше приложение будет показывать вопросы и давать водопользователю выбрать один ответ.
Сами вопросы будут в виде json. Формат:

{
    "id": "Уникальный индификатор",
    "request": "Текст вопроса",
    "items": {
        "1": "Вариант ответа 1",
        "2": "Вариант ответа 2",
        "3": "Вариант ответа 3"
    },
    "select": null
}   

Как легко догадаться поле «select» будет хранить выбранное значение.
Теперь html:

<!DOCTYPE html>
<html>
<head>
    <script src="js/vendor/jquery.min.js"></script>
    <script src="js/vendor/underscore-min.js"></script>
    <script src="test.js"></script>
    <title></title>
</head>
<body>
    <!-- Выводим вопросы -->
    <div id="out">
        <div class="show"></div>
        <div class="page">
            <br>
            <a class="prev_question" href="#">< Назад </a> &nbsp;
            <a class="next_question" href="#"> Назад  > </a>
        </div>
    </div>

    <!-- Шаблон вопроса -->
    <script id="temlate-test" type="text/template">
        <h2><%= request %></h2>
        <% _.each(items, function(caption, key){ %>
            <input type="radio"  name="<%= id %>" id="<%= id+key %>" value="<%= key %>" <% if(select==key) print('checked')%>>
            <label for="<%= id+key %>"> <%= caption %> </label>
            <br>
        <% }); %>
    </script>

    <!-- скрипт запускающий тест -->
    <script type="text/javascript">
        var data = [
            {
                "id": "r1",
                "request": "Вопрос 1",
                "items": {
                    "1": "Ответ 1",
                    "2": "Ответ 2",
                    "3": "Ответ 3"
                },
                "select": null
            },
            {
                "id": "r2",
                "request": "Вопрос 2",
                "items": {
                    "1": "Ответ 4",
                    "2": "Ответ 5",
                    "3": "Ответ 6"
                },
                "select": null
            }
        ];

        $(document).ready(function(){
            var test = new appTest({
                'bebug':true,
                'data': data
            })
            test.PageLoad();
            test.render();
        });


    </script>
</body>
</html>  

а теперь сам js скрипт:

var appTest = function (options) {
    var self = this;

    // jQuery элемент к которому привязываем все собятия
    var elem = undefined;
    // текущий вопрос
    this.question_cur = 0;
    // Максимум вопросов
    this.question_max = undefined;

    // Настройки по умолчанию
    this.option = {
        // отладка
        bebug: false,
        // селект для this.elem
        elem: '#out',
        // данные из вне
        data: []
    }

    // Подбираем параметры
    $.extend(this.option, options);

    /**
     * Вызывается после загрузки страницы
     * Служит для инициализации
     */
    this.PageLoad = function()
    {
        elem = $(self.option.elem);
        this.question_cur = 0;
        this.question_max = self.option.data.length - 1;

        // --- подвязывваем DOM события тригер
        elem.on('click', '.prev_question', {event: 'question:prev'}, callTrigger);
        elem.on('click', '.next_question', {event: 'question:next'}, callTrigger);

        // --- подвязываем события
        elem.on('question:prev', eventQuestionPrev);
        elem.on('question:next', eventQuestionNext);
    }

    /**
     * Логирование.
     * Зависит от включенного "option.bebug"
     */
    this.log = function(){
        if(!this.option.bebug) return;

        var args = Array.prototype.slice.call(arguments);

        try {
            console.log.apply(console, args);
        } catch (e) {
            console.log(args.join(' '));
        }
    }

    /**
     * Вызвать тригер на событие
     * e.data.event - Название тригера
     * @param e event
     */
    function callTrigger(e) {
        e.preventDefault();
        var params = {
            type: e.data.event,
            target: e.target,
            context: this
        };

        self.log(params);
        elem.trigger(params);
    }

    /**
     * Отрисовка вопроса
     */
    this.render = function()
    {
        this.renderQuestion();
        this.renderPage()
    }

    /**
     * Выводим текущий вопрос
     */
    this.renderQuestion = function()
    {
        this.log(this.option.data);
        var item = this.option.data[this.question_cur];         // текущий вопрос
        var tmplItem = _.template($('#temlate-test').html());   // вызываем шаблонизатор
        $('.show',elem).html(tmplItem(item));                   // выводим
    }

    /**
     * Выводим нумератор страниц
     */
    this.renderPage = function()
    {
        var $page = $('.page',elem);
        $('.prev_question', $page).prop( "disabled", this.question_cur == 0 );
        $('.next_question', $page).prop( "disabled", this.question_cur == this.question_max );
    }

    function sevaCurSelect()
    {
        var item = self.option.data[self.question_cur];
        item.select = $('input:checked').val();
    }

    /**
     * Предыдущий вопрос
     * @param e
     */
    function eventQuestionPrev(e)
    {
        if(self.question_cur > 0){
            sevaCurSelect();
            self.question_cur--;
            self.render();
        }
    }

    /**
     * Следующий вопрос
     * @param e
     */
    function eventQuestionNext(e)
    {
        if(self.question_cur < self.question_max){
            sevaCurSelect();
            self.question_cur++;
            self.render();
        }
    }
}
&#91;/code&#93;

Теперь непосредственно разбор.
&#91;code lang="javascript"&#93;
var self = this;
var elem = undefined;
&#91;/code&#93;
Все значения объявлены через <strong>var</strong> являются локальными (внутри объекта), а потому недоступны из вне.
В данном случае создали переменную self к которой будем обращаться когда контекст this указывает не на appTest.
elem  - переменная указывающая на jQuery объект в который выводим данные. Устанавливается при инициализации параметров в PageLoad.


    // Настройки по умолчанию
    this.option = {
        // отладка
        bebug: false,
        // селект для this.elem
        elem: '#out',
        // данные из вне
        data: []
    }

    // Подбираем параметры
    $.extend(this.option, options);

Сами параметры. Вернее их значения по умолчанию.

  • bebug - включает режим отладки (выводит в консоль информацию).
  • elem - селектор для переменной var elem.
  • data - данные для тестирования.
  • $.extend(...) - объединяет параметры переданные при создание класса (options) с параметрами по умолчанию.

PageLoad вызывается после загрузки страницы. Служит для инициализации объекта и обработки событий.

elem.on('click', '.prev_question', {event: 'question:prev'}, callTrigger);
elem.on('question:prev', eventQuestionPrev);

Вешает на клик по объекту с классом "prev_question" вызов триггера с именем "question:prev"
Затем на событие "question:prev" назначается функция "eventQuestionPrev".
Такой подход хорош тем что все события с DOM HTML "подвязываются" в одном месте. Но в случае необходимости можно вызвать это событие из другого скрипта. Достаточно послать на корневой объект (elem) событие "question:prev"

Исходники на GitHub

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *