Пожалуйста, опишите ошибку

Нашли баг? Помогите нам его исправить, заполнив эту форму

Просто и понятно о Polymer. Data-bindings.

Алексей Танкаян
Web-разработчик

В этой статье мы рассмотрим, как работают data-bindings в Polymer. Понимание работы основных механизмов делает отладку приложения более простой и проясняет, как правильно нужно пользоваться этой библиотекой. Здесь мы углубимся в то, как Polymer работает с observers, one-way bindings и two-way bindings. Но прежде, рекомендуем ознакомиться с первой статьей нашего цикла, где «на пальцах» объясняется, как работать с библиотекой, если ранее не имели с ней дело.

Итак, начнем. Существует два типа binding-annotations:

  1. One-way annotation (‘[[]]’) — позволяет передавать данные из хост-элемента к дочернему, но никогда не передаёт из дочернего к хост-элементу.
  2. Two-way annotation (‘{{}}’) — позволяет передавать данные от хост-элемента к дочернему и наоборот.

Разберёмся, что же происходит тогда, когда Polymer работает с этими аннотациями.

Когда компонент регистрируется, Polymer сканирует содержимое тэга template этого компонента и внутри него ищет те элементы, у которых есть binding-annotations. Затем, Polymer создаёт для каждого элемента так называемые «property-effects». Property-effect — это такой объект, который содержит в себе информацию о том, какой элемент нужно обновить, когда меняется значение property, указанного внутри binding-annotation.

<dom-module id="x-demo">
    <template>
        <span>{{demo}}</span>
        <x-input type="text" value="{{demo}}"></x-input>
    </template>
    <script>
        Polymer({
            is: 'x-demo' 
        });
    </script>
</dom-module>

Например, для этого компонента Polymer создаст property-effect для элемента span и другой property-effect для элемента input. Все созданные property-effects хранятся в массиве, представляющий из себя список действий, который должен выполниться, когда изменяется property.

Можно посмотреть, что из себя представляют эти property-effects, обратившись к приватному свойству компонента _propertyEffects.

Здесь мы можем увидеть, что property-effect содержит в себе множество служебной информации, необходимой для определения того, что должно быть обновлено, при каком событии оно должно быть изменено и т.п.

Помимо создания property-effects, Polymer также создаёт что-то вроде сеттера для property. Внутри этого сеттера выполняется так называемая «грязная проверка» (dirty-checking), которая представляет собой проверку на то, изменилось ли вообще значение property.

Это выглядит примерно следующим образом (Polymer описывает своё подобие сеттеров и делает это немного иным образом, здесь и далее они представлены в таком виде для простоты понимания):

...
Polymer({
    is:'x-demo',
    set demo(val) {
        var old = this.__data__.demo;
        if (old !== val) {
            this.__data__.demo = val;
        }
        //run all property effects
    }
})
...

__data__ — это приватная переменная, где хранятся значения properties элемента. Если значение property изменилось, то после сохранения нового значения в __data__, обходятся все property-effects и выполняются соответствующие действия для обновления элементов, связанных с property при помощи binding-annotations.

Теперь разберём, что будет, если мы для property укажем observer и добавим флаг notify со значением true:

...
Polymer({
    is:'x-demo',
    properties: {
        demo: {
            observer: '_demoChanged',
            notify: true
        }
    }
})
...

На самом деле, никакой магии не произойдёт. После выполнения dirty-checking’а и property-effects, вызовется функция-observer. А затем отправится событие, уведомляющее об изменении property.

<dom-module id="x-demo">
    <template>
        <span>{{demo}}</span>
        <x-input value="{{demo}}"></x-input>
    </template>
    <script>
        Polymer({
            is:'x-demo',
            properties: {
                demo: {
                    observer: '_demoChanged',
                    notify: true
                }
            },
            set demo(val) {
                //dirty-checking
                //run all property effects
                this._demoChanged();
                this.fire('demo-changed', { value: val });
            }
        })
    </script>
</dom-module>

Разберём подробнее, что делает флаг notify. Каждый раз когда мы указываем для него значение true, Polymer в сеттер добавляет отправку события, которое сообщает другим компонентам, что property изменилось. Это событие всегда будет называться ‘%propertyname%-changed’ и будет хранить новое значение property. Также Polymer после создания property-effects проверит, связано ли property (в нашем случае это demo) с property другого компонента (в нашем случае это свойство value компонента x-input). В таком случае, внутри компонента будет создан обработчик, слушающий событие value-changed, который будет менять значение demo, с которым было связано свойство value компонента x-input.

Если в темплейте компонента мы используем one-way annotation:

<dom-module id="x-demo">
    <template>
        <span>{{demo}}</span>
        <x-input value="[[demo]]"></x-input>
    </template>
    <script>
       ...
    </script>
</dom-module>

В таком случае, Polymer просто не будет создавать обработчик, слушающий событие ‘value-changed’.

Если для property указать флаг readOnly со значением true:

...
Polymer({
    is:'x-demo',
    properties: {
        demo: {
            readOnly: true
        }
    },
    ...
})

...

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

...
Polymer({
    is:'x-demo',
    properties: {
        demo: {
            readOnly: true
        }
    },
    _setDemo(val) {
        //dirty-checking
        //run all property effects
    }
})
...

На этом все основы работы data-binding’ов в Polymer раскрыты, но есть ещё немало особенностей работы биндингов с массивами, объектами, computed properties и многим другим. Более подробно вы можете почитать об этом здесь: https://www.polymer-project.org/1.0/docs/devguide/data-binding.

Читать и комментировать

Краснодар

Коммунаров, 268,
3 эт, офисы 705, 707

+7 (861) 200 27 34

Хьюстон

3523 Brinton trails Ln Katy

+1 833 933 0204

Москва

+7 (495) 145-01-05