Динамическое связывание данных в HTML и JS

    Всем доброго времени суток.
    Если вы задаетесь одним из следующих вопросов:
    • что такое динамическое связывание данных?
    • как работает связывание данных в AngularJS или ему подобных MVVM-фреймворках?
    • чем, черт возьми, MPV отличается от MVVM?

    Тогда вам под кат…
    И да… в конце, как всегда, ссылка на код ;)

    Про MVP:
    MVP (Model-View-Presenter) — это один из самых распространенных шаблонов проектирования UI.

    Суть его заключается в следующем:
    • Presenter подписывается на события от View
    • View эмитит события
    • Presenter ловит события и делает запросы в Model
    • При получении ответа от Model, Presenter обновляет View

    Сразу же в глаза бросается ключевое отличие MVP от MVC: MVP, в отличии от MVC, имеет двустороннюю связь с View.
    Запомним и пойдем дальше…

    Про MVVM:
    MVVM (Model-View-ViewModel) — это улучшеная форма MVP, при чем грань между ними так тонка, что иногда думаешь: «О, небо! За что ты так со мной?»

    Сейчас объясню что я имею ввиду.

    Суть MVVM заключается в следующем:
    • ModelView подписывается на события от View
    • View эмитит события
    • ModelView ловит события и делает запросы в Model
    • При получении ответа от Model, ModelView обновляет View




    Если отбросить формальности, то так оно и есть. Поэтому я и сказал, что грань между MVP и MVVM очень тонка.

    Глобально, разница лишь в том, что MVVM реализует более гибкий слушатель событий от View.
    При чем реализует таким образом, что становится доступным так называемое декларативное динамическое связывание данных.

    Про динамическое связывание данных:
    Это такой механизм, при котором, изменив значение модели с любой стороны(со стороны View или Model), это изменение моментально вступит в силу. То есть, изменив значение в Model (в MVVM — ViewModel частично берет на себя функцию модели), оно сразу отобразится во View и наоборот.

    Вы можете спросить: «Если в MVP есть двусторонняя связь между View и Presenter, то почему мы не можем реализовать динамическое связывание данных на MVP?».

    Ответ очень прост — можем!
    По сути, MVP уже подразумевает динамическое связывание данных в той или иной степени.
    И, если MVP это чисто императивный подход к связыванию данных, то MVVM — декларативный.

    Вот и вся разница.
    Но суть у них одна и та же!

    Про реализацию:
    Теперь рассмотрим вопросы, связанные с реализаций динамического связывания данных.
    Начнем с того, что, на текущий момент, браузер не способен динамически отслеживать изменение значений в переменных.
    Конечно, есть такая штука как Object.observe(), но эта вещь, пока еще, не является частью стандарта.
    Поэтому исходим из того, что
    браузер не способен динамически отслеживать изменение значений в переменных

    Соответсвенно, необходимо как-то понимать: когда нужно произвести синхронизацию между Model и View.
    В современных фреймворках, типа Angular или Knockout, к этому вопросу подходят очень просто: вешают слушатели на разные события от элемента, которому необходимо динамическое связывание данных.
    Например, для text input вешается слушатель на событие keyup.
    Для button — click
    И т.д.

    Внутри обработчика происходит чтение новых данных и затем запускается механизм синхронизации оных с Model.

    Вот, собственно, и вся история.

    Кстати, если вы используете Angular, то, скорее всего, вам очень часто приходится прибегать к использованию таких вещей, как сервис $timeout…
    Если вы используете $timeout на автоматизме, потому что так написано где-то на stack overflow, но не понимаете его сути, то знайте, что $timeout ждет, пока закончится текущий $digest-цикл, затем выполняет код, который вы ему передали, и затем снова запускает $digest-цикл. Именно так и достигается обновление данных, если оно было инициировано не из внутренностей Angular.

    Что такое $digest-цикл?
    В AngularJS это как раз и есть процесс синхронизации значений между Model и View.

    И, как обещал, ссылка на Gist, в котором реализовано простейшее динамическое связывание данных.

    В нескольких словах:
    • выбираем все элементы, у которых есть атрибут data-bind
    • регистрируем эти элементы как слушатели изменений модели
    • вешаем на document общий обработчик, который будет производить синхронизацию между View и Model


    Спасибо за внимание.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 28

      +3
      На tutorial не тянет ((
        +2
        Не соглашусь — на том уровне, на котором идёт речь — вполне доходчиво и ярко расписано. Картинку. правда, можно под спойлер, потому что не у всех на работе стоит адблокер.)

        Контрольный вопрос к туториалу:

        1. Взяли Backbone, который реализует MVP и добавили к нему плагин, который реализует двустороннее декларативное связывание данных — Backbone.ModelBinding. Вопрос: станет ли этот комплект достаточным для того, чтобы проект считался MVVM? Что надо написать в подтверждение? (Для полной постановки тут целый проект надо придумать.)

        (Вот таких тройки вопросов после статьи, в самом деле, не хватает, но и за один пример автору спасибо )).
        +3
        Конечно, есть такая штука как Object.observe(), но эта вещь, пока еще, не является частью стандарта.
        Или уже, т.к. автор фичи в прошлом году грозился её закрыть и вырезать её из хрома.

        Например, для text input вешается слушатель на событие keyup.
        Используется событие «input» (keyup может применяться для поддержки IE6, хотя ангуляр с ним не дружит)

        что $timeout ждет, пока закончится текущий $digest-цикл, затем выполняет код
        Тут используется setTimeout, он уже и вызывает код когда другой код не выполняется (один поток же)

        Что такое $digest-цикл?
        В AngularJS это как раз и есть процесс синхронизации значений между Model и View.
        Скорее между ViewModel и View, но все равно это частный случай, $digest-цикл — это процесс поиска изменений (dirty-checking) данных и вызов подписчиков (обработчиков). А что они будут синхронизировать (или не будут) это уже дело десятое.
          0
          Object.observe отменили в пользу Proxy, на которых можно реализовать то же самое.
            0
            Согласен. Но, опять же, пока что это не входит в утвержденный стандарт ;)
              0
                0
                Только в ES6.
                На данный момент, ES6 поддерживается ох как не везде )
                  0
                  Даже последний хром(не Canary) ничего не знает про Proxy
                    0
                    Ну так мы и говорим про стандарт. Object.observe выкинули, потому что есть Proxy. Proxy есть в ES6. ES6 — утвержденный стандарт. Таким образом, утверждение «Но, опять же, пока что это не входит в утвержденный стандарт ;)» не соответствует истине.
                      0
                      Да, извиняюсь. Перефразирую: Proxy входит в утвержденный стандарт ES6. Но сам ES6, судя по http://kangax.github.io/compat-table/es6/, полностью нигде не поддерживается
                        0
                        Ну так и Object.observe тоже далеко не везде работает.
                        Proxy работает в Edge, FF, последнем V8 и следующих хроме и опере. Я думаю, что в nodejs 6 тоже будет.
                          0
                          Ну так и Object.observe тоже далеко не везде работает.

                          Ну так и я о том же.

                          следующих хроме и опере. Я думаю, что в nodejs 6 тоже будет.

                          Надеюсь :)
                            0
                            Короче, «умер максим Object.observer — ну и фиг с ним», да здравствует Proxy:)
                0
                Не совсем тоже самое, у каждого свои минусы, когда O.O работает с самим объектом, с Proxy вам придется работать с proxy интсансом, а не исходным объектом. В итоге если вам нужно отслеживать изменения в объекте который вам прислали из вне, вам нужно сделать для него proxy обертку и как то передать обратно (т.е. источник должен это поддерживать), с О.О такой проблемы нет.
                  0
                  Ну народ как-то вроде полифиллит O.o на Proxy.
                    0
                    Посмотрел 2 первых попавшихся O.O полифила, один сделан через defineProperty + setTimeout, второй просто через setTimeout.
                    Где вы видели полифил на Proxy?
                      0
                      https://www.npmjs.com/search?q=object.observe, второй пакет сверху.
                        0
                        3. The variables pointing to objects that are being observed must be re-assigned to point to a proxy returned by the call to Object.observe, e.g.
                        Про что я выше и написал, что придется работать с proxy оберткой, а не с исходным объектом.
                        вам нужно сделать для него proxy обертку и как то передать обратно
                          0
                          Да я, собственно и не отрицал.
                          Суть-то в том, что можно реактивно реагировать на изменения в объекте. А уж как это реализовано — все равно, лишь бы не как в ангуляре:)
                            0
                            можно реактивно реагировать на изменения в объекте
                            Многие фреймворки всю реактивность складывают в «беклог» и по таймауту (либо nextTick) вызывают обработчики (это «защищает» при множественном изменении одной переменной), кстати O.O как раз по дефолту так и работает, так что «немедленная» реактивность не так уж и нужна.
                            все равно, лишь бы не как в ангуляре:)
                            Подход ангуляра не так уж и плох (есть свои плюсы), при том что это один из самых быстрых методов для большинства UI (см. бенчмарки).
                              –1
                              В чем проблема реализации ангуляра?

                              P.S. Реактивное программирование — это совсем другое. Так, к слову.
                0
                lega
                Используется событие «input» (keyup может применяться для поддержки IE6, хотя ангуляр с ним не дружит)

                В целом — да, согласен.

                Тут используется setTimeout, он уже и вызывает код когда другой код не выполняется (один поток же)

                В итоге — да. Но судя по исходникам, последовательность получается такая:

                • ждет, пока закончится текущий потом выполнения ($browser.defer — это суть setTimeout)
                • выполняет наш код
                • вызывает $rootScope.$apply()

                Опять же судя по исходникам, $apply(), помимо прочего, пытается запустить $digest

                Скорее между ViewModel и View, но все равно это частный случай, $digest-цикл — это процесс поиска изменений (dirty-checking) данных и вызов подписчиков (обработчиков). А что они будут синхронизировать (или не будут) это уже дело десятое.

                всё верно ;)
                  0
                  Кто ангуляр используется знает что лучше вызвать scope.$digest чем $apply если известно что изменения касаются только конкретного scope. То есть запускается обычный таймаут вместо $timeout и потом внутри scope.$digest. А есть еще $evalAsync метод.
                    0
                    запускается обычный таймаут вместо $timeout и потом внутри scope.$digest

                    и чем это тогда будет отличаться от вызова $timeout? :)
                      0
                      ну $timeout ведь $rootScope дергает
                        0
                        Ну так нет ничего страшного в инъекциях. Инжектор в Angular достаточно умен, чтобы не делать лишних действий.
                        Дело конечно ваше: можете использовать и обычный setTimeout с вызовом $digest внутри него.
                        В статье я просто рассказал про устоявшийся в Angular паттерн c $timeout
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      MVP, MVVM… Еще несколько лет назад это называлось MVC with data bindings!

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое