Что такое two way binding

Двустороннее связывание Angular, чуть больше понимания

От переводчика: два года назад я начал свой первый проект на Angular(2+), имея большой и успешный бэкграунд AngularJS. Переход потребовал заметного форматирования мышления, поскольку слишком много на A1 и A2+ делается «чуть-чуть по другому». Болезненность перехода мне заметно снизил блог thoughtram. Я ещё год назад получил разрешение перевести эту статью «об элементарном и всем легко понятном». Но руки они такие руки (своих статей пачка недописанных). Что удивительно, статья неплохо переводится гугл транслейтом. Но некоторые нюансы в этом переводе терялись, не говоря об авторском стиле. Авторский стиль не сохранился в полной мере и в моей версии. Но, надеюсь, настроение и мысли статьи мне удалось передать.

Я понимаю, что Angular не самая востребованная тема на Хабре, но надеюсь, что перевод поможет кому-то, так же как исходная статья помогла когда-то мне.

Вот что вызывало вау-эффект в старом добром AngularJS, так это «двустороннее связывание». Эта магия мгновенно влюбляла в AngularJS, и ломала все представления о скучном программировании страниц и (о, ужас!) веб-форм. Изменения в данных мгновенно отображались на экране и наоборот. Те, кто раньше разрабатывал приложения на jQuery, воспринимали связывание, как попадание в сказку. А бородатые монстры, пилившие толстых клиентов ещё до jQuery, начинали судорожно пересчитывать бездарно потерянные человеко-месяцы.

И, более того, магия двустороннего связывания была доступна не только для специальных нотаций и избранных компонентов. Мы могли легко её использовать в наших собственных директивах и компонентах (просто установив параметр конфигурации).

В Angular2+ создатели отказались от встроенной двусторонней привязки данных (кроме как через ngModel). Но это не означает, что мы не можем использовать двустороннее связывание в собственных директивах… Просто халява кончилась и теперь нужно кое-что делать самостоятельно. И, желательно, c пониманием того, как оно устроено в Angular.

Оглавление

Двустороннее связывание в двух словах

В A2+ только одна единственная директива реализует двустороннюю привязку данных: ngModel. И на первый взгляд, это та же магия, что и в AngularJS (только в другой нотации). Но что под капотом?

Как ни удивительно, под капотом всё относительно просто и логично: двустороннее связывание сводится к привязке свойств и привязке событий. Две односторонние привязки, вместо одной двусторонней? Хорошо, давайте две.

Да-да, это прекрасное и удивительное демо Angular2 от 2009 года. Без шуток, прекрасное. При изменении поля, значение username попадает в модель, и тут же отражается в приветствии на форме.

Но как это работает? Напомним, что двустороннее связывание в Angular2 это привязка свойств и привязка событий. И да, они могут быть одновременно доступны в одной директиве. Более того, даже без ngModel, мы легко могли бы реализовать двустороннее связывание данных. Например, так:

C выводом <> понятно, но что там понаписано в input? Давайте разбираться:

Мы связываем свойство username модели Angular со свойством value элемента ввода браузера (односторонне связывание из модели в представление).

Мы также привязываем к событию input нашего элемента выражение. Которое присваивает значение $event.target.value свойству username модели.

А что такое $event.target.value? Как уже упоминалось, $event полон различной небесполезной информации о событии. В данном случае это InputEventObject, в котором свойство target ссылается на элемент DOM, который иннициировал событие (т.е. наш элемент ввода).

Итак, всё, что по сути мы делаем – читаем содержимое (value) элемента ввода ($event.target), когда пользователь вводит значение. И когда мы присвоим это значение username, данные представления отправятся в модель.

Вот и всё. Это и есть «двустороннее связывание в двух словах». Красота?

Но когда же ngModel вступит в игру? Сценарий работы с элементами ввода очень распространен и востребован. И почему-то хочется иметь директиву, которая прячет реализацию и спасает от лишних нажатий клавиш.

Понимание ngModel

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

Практически всё то же самое. Привязка свойства [ngModel] заботится об обновлении значения элемента ввода. Привязка события (ngModelChange) уведомляет мир, что происходят изменения в DOM.

А вы заметили, что выражение-обработчик использует только $event, а не $event.target.value. Что-то тут не так? Отнюдь. Как сказано выше, $event это синтетическая переменная, которая несёт полезную нагрузку. Решение, что считать полезным берёт на себя Angular. Другими словами, ngModelChange берет на себя извлечение target.value из внутренней $event и просто отдаёт нам то, что мы и хотим, без упаковки и бубна. Если быть технически точными, эти занимается DefaultValueAccessor: это он занимается извлечением данных и переносом их в базовый объект DOM, хотя,… можно просто про это не думать).

И последнее, но не менее важное: поскольку писать username и ngModel дважды все-таки излишне, Angular допускает использование сокращенного синтаксиса [()], также называемого «банан в коробке». Что аналогично предыдущему примеру, и возвращает нас к примеру из начала раздела, но уже с пониманием реализации ngModel. Обеспечивающей то самое двустороннее связывание.

Создание собственных двусторонних привязок данных

Теперь мы знаем достаточно, чтобы создавать собственные двусторонние привязки данных. Все, что нужно сделать, просто следовать тем же правилам, что и ngModel, а именно:

Но, допустим, мы создаем пользовательский компонент счетчика (и не хотим использовать пользовательский элемент управления формы).

У нас есть свойство компонента counter для отображения текущего значения счетчика. Чтобы обеспечить ему двустороннюю привязку, первое, что нужно сделать, — это превратить его в Input параметр. Для этого очень кстати декоратор @Input():

Это уже позволяет привязать свойство компонента к потребителю следующим образом:

Теперь нам нужно задать @Output() событие с тем же именем (counter) и суффиксом Change (получается counterChange). Мы хотим возбуждать это событие при каждом изменении counter. Для чего добавим @Output() свойство. И добьём, в пару геттеру, сеттер counter, в котором мы будем перехватывать изменение значения и выкидывать событие с актуальным значением счётчика:

Это оно! Теперь мы можем привязать выражение к этому свойству, используя двусторонний синтаксис привязки данных:

Проверьте демо и попробуйте!

Заключение

Angular больше не поставляется со встроенной двусторонним связывание данных. Взамен «в коробке» есть API-интерфейсы, которые позволяют реализовать полное связывание как связывание свойств и событий.

ngModel поставляется как встроенная директива двустороннего связывания в FormsModule (не забудьте добавить его в секцию imports объявления @NgModule: прим. пер). Связывание через ngModel должно быть предпочтительным при создании компонентов, которые служат в качестве пользовательских элементов управления форм. В остальном всё зависит от ваше фантазии.

PS от переводчика: реализация связывания в A2+ стала более современной. Теперь для наблюдения за изменениями «по феншую» используется почти «бесплатные» сеттеры (хотя понятно, что механизмы для dirty-checking остались, как минимум для высокоуровневых пользовательских компонентов). Это позволило отказаться от 100500 watcher’ов (процедур следящих за изменениями «своих» данных). Которые в A1 любили создавать злобную нагрузку на браузер и требовали необычайно прямых рук при планировании насыщенных интерактивных страниц.

При правильно спроектированных компонентах, A2 «из коробки» стал значительно более «отзывчивым». Пусть и за счёт труда программистов. Теперь можно разместить легион компонентов на странице и не беспокоится за ресурсы процессора.

Обратной стороной медали стала начальная стоимость «процесса входа» в A2+, что повлияло на популярность фреймворка. Но и у A1 была высокая стоимость входа, только она была отнесена на высшую лигу. Из-за непонимания как организовывать большие приложения, многие прототипы «взлетевшие» на A1, потом «рассыпались» и переписывались на React и Vue.

Надеюсь, что этой статьёй я помогу немного снизить порог для начального входа в A2+, который продолжает оставаться востребованным (о чём я знаю не понаслышке).

Источник

Gaperton’s blog

Руководство по two-way data binding в React.

Во-первых, он таки в React есть. Называется value link. Это такой дизайн паттерн. И не смотря на то, что Фейсбук убирает свою кривую реализацию оного из React, он не может вам запретить его использовать.

Понимаем, что это такое, и учимся делать формы на React просто и приятно.

Во-вторых, этот паттерн способен на куда большее, чем примитивная его реализация в React. Например, он помогает весьма изящно делать валидацию ввода в реальном времени.

Учимся делать валидацию форм красиво, без традиционной для таких случаев боли:

Ну и в третьих. Самое интересное в этом паттерне вовсе не тот очевидный факт, что он устраняет ненужные коллбэки. А то, что он позволяет полностью отделить (и инкапсулировать) способ, которым выполняется data binding, как от слоя view, так и от слоя данных. Чего вообще ни один из популярных фреймворков не может.

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

Учимся работать со сложным состоянием:

Comments:

Руководство по two-way data binding в React.

какие-то убойные ссылки вы подсунули, не просто браузер схлопывается а весь айпад перегружается 🙂

реакт я не видела, а ангулар меня не впечатлил, хотя мы взяли оттуда пару идей
но наш подход propriatery и вряд ли он когда выйдет за пределы компании

Edited at 2016-06-05 01:05 pm (UTC)

> видимо пора выходить на публику с нашим подходом

Выходите на здоровье, за чем дело встало. Публика оценит. Только в своих постах, пожалуйста.

Edited at 2016-06-05 05:09 pm (UTC)

мучайтесь дальше с вашим реактом 🙂

Нахуй пошел из моих комментов. Вместе со своими блевничками, фреймворками, самомнением, и оценочными суждениями. Вежливости и профессиональному любопытству он меня учить собрался, смотрите. 🙂

Edited at 2016-06-05 09:09 pm (UTC)

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

Edited at 2016-06-08 06:26 am (UTC)

Выглядит, конечно симпатично, но есть вопрос:

И в качестве оффтопика: пробовали ли вы mobx? Мы в текущий проект его засунули, и он вроде и работает, но реактивность местами создает неожиданные трудности (например, при копировании объектов реактивность теряется и нужно делать закат солнца вручную).

Можно вынести разделяемый стейт в состояние корневого компонента, и передавать вниз линки на его части (как в статье номер 3). Но в целом да. Большое приложение на одних линках собирать мягко говоря не очень удобно. Потребуется стейт-контейнер, который может побольше, чем raw react component state.

И он у нас есть. Вот он: https://github.com/Volicon/NestedTypes А вот его привязка к React, где он используется вместо React state. https://github.com/Volicon/NestedReact Линки в данном случае делаются на его элементы.

> И в качестве оффтопика: пробовали ли вы mobx?

Помимо определения глубоких изменений (которые у нас определяются по другому, и обладают транзакционной семантикой, в рамках которой наблюдатели могут добавлять к транзакции изменения, и это будет определено как одно изменение), NestedTypes:

— содержит механизм сериализации, который умеет, в частности, работать с рекурсивными данными вроде деревьев, и отношениями по id. https://github.com/Volicon/NestedTypes/blob/master/docs/RelationsGuide.md

— является «динамически типобезопасным». То есть, он при присваивании в аттрибуты приводит типы, так, что сломать протокол невозможно. Например, если аттрибут Boolean, то там будет всегда лежать Booolean. И на сервер всегда уйдет Boolean.

В наших продуктах (это здоровенный SPA на 100К строк кода) ни flux, ни React state вообще не используется. Только вот это, как единая техника работы с состоянием.

Edited at 2016-06-06 10:39 pm (UTC)

Источник

What is two way binding?

I have read lots that Backbone doesn’t do two way binding but I don’t exactly understand this concept.

Could somebody give me an example of how two way binding works in an MVC codebase and how it does not with Backbone?

6 Answers 6

Two-way binding just means that:

Backbone doesn’t have a «baked-in» implementation of #2 (although you can certainly do it using event listeners). Other frameworks like Knockout do wire up two-way binding automagically.

Что такое two way binding. Смотреть фото Что такое two way binding. Смотреть картинку Что такое two way binding. Картинка про Что такое two way binding. Фото Что такое two way binding

In Backbone, you can easily achieve #1 by binding a view’s «render» method to its model’s «change» event. To achieve #2, you need to also add a change listener to the input element, and call model.set in the handler.

Here’s a Fiddle with two-way binding set up in Backbone.

Что такое two way binding. Смотреть фото Что такое two way binding. Смотреть картинку Что такое two way binding. Картинка про Что такое two way binding. Фото Что такое two way binding

Two-way binding means that any data-related changes affecting the model are immediately propagated to the matching view(s), and that any changes made in the view(s) (say, by the user) are immediately reflected in the underlying model. When app data changes, so does the UI, and conversely.

This is a very solid concept to build a web application on top of, because it makes the «Model» abstraction a safe, atomic data source to use everywhere within the application. Say, if a model, bound to a view, changes, then its matching piece of UI (the view) will reflect that, no matter what. And the matching piece of UI (the view) can safely be used as a mean of collecting user inputs/data, so as to maintain the application data up-to-date.

A good two-way binding implementation should obviously make this connection between a model and some view(s) as simple as possible, from a developper point of view.

It is then quite untrue to say that Backbone does not support two-way binding: while not a core feature of the framework, it can be performed quite simply using Backbone’s Events though. It costs a few explicit lines of code for the simple cases; and can become quite hazardous for more complex bindings. Here is a simple case (untested code, written on the fly just for the sake of illustration):

This is a pretty typical pattern in a raw Backbone application. As one can see, it requires a decent amount of (pretty standard) code.

AngularJS and some other alternatives (Ember, Knockout…) provide two-way binding as a first-citizen feature. They abstract many edge-cases under some DSL, and do their best at integrating two-way binding within their ecosystem. Our example would look something like this with AngularJS (untested code, see above):

But, be aware that some fully-fledged two-way binding extensions do exist for Backbone as well (in raw, subjective order of decreasing complexity): Epoxy, Stickit, ModelBinder…

One cool thing with Epoxy, for instance, is that it allows you to declare your bindings (model attributes view’s DOM element) either within the template (DOM), or within the view implementation (JavaScript). Some people strongly dislike adding «directives» to the DOM/template (such as the ng-* attributes required by AngularJS, or the data-bind attributes of Ember).

Taking Epoxy as an example, one can rework the raw Backbone application into something like this (…):

All in all, pretty much all «mainstream» JS frameworks support two-way binding. Some of them, such as Backbone, do require some extra work to make it work smoothly, but those are the same which do not enforce a specific way to do it, to begin with. So it is really about your state of mind.

Also, you may be interested in Flux, a different architecture for web applications promoting one-way binding through a circular pattern. It is based on the concept of fast, holistic re-rendering of UI components upon any data change to ensure cohesiveness and make it easier to reason about the code/dataflow. In the same trend, you might want to check the concept of MVI (Model-View-Intent), for instance Cycle.

Источник

Angular — Custom two way data binding

Что такое two way binding. Смотреть фото Что такое two way binding. Смотреть картинку Что такое two way binding. Картинка про Что такое two way binding. Фото Что такое two way binding

Dec 3, 2017 · 3 min read

Unlike AngularJS, Angular does not provide two way binding by default, which avoids all the digest cycle and watchers issues that AngularJS dealt with. But sometimes, there would be a need for a two way data binding between two components — Parent and its child. This article demonstrates how to create two way data binding between two components.

Before diving into implementing custom two way binding, we should understand the binding concepts below:
* Property Binding
* Event Binding

Property Binding

When you pass the dat a property of a component as an attribute within the square brackets [] to the target element in html, it is called Property Binding.

Event Binding

A component can listen to an event raised by another component by adding the target event within parenthesis () in the target element. It is called Event Binding.

Что такое two way binding. Смотреть фото Что такое two way binding. Смотреть картинку Что такое two way binding. Картинка про Что такое two way binding. Фото Что такое two way binding

Let’s dig into this with an example. Let’s create a custom ParentComponent with a property amount in it and provide a button to the user to deposit more amount. Clicking the button would increase the amount value.

Next, let’s add a custom ChildComponent and pass the property amount from child to parent. Remember Property Binding? Yes, we are going to add a property binding between parent and child component using @Input() decorator.

Thats it, Property binding is done. When ParentComponent updates the amount, ChildComponent would know it.

Next, let’s add a method in ChildComponent to withdraw the money from the balance and provide the user a button to withdraw money.

Now the ChildComponent could withdraw money but ParentComponent would not know about it. How do we fix it? Event Binding to the rescue! Let’s make ChildComponent emit an event when it updates the amount property using @Output() decorator.

Now make ParentComponent listen to the amountChange event and update its amount property

Tadaa… Two-way binding done!

Banana in a box

Lets modify the syntax a bit in Parent’s template like below:

A downside of [()]

But we could not do the same in ParentComponent because ngOnChanges is triggered only when @Input() is changed (parent to child). In that case, we would want to split the banana-in-a-box back to the property and event binding syntax and do the extra processing in ParentComponent ‘s event listener method.

Источник

Two-way Binding Helpers

Importing

LinkedStateMixin is an easy way to express two-way binding with React.

In React, data flows one way: from owner to child. We think that this makes your app’s code easier to understand. You can think of it as «one-way data binding.»

However, there are lots of applications that require you to read some data and flow it back into your program. For example, when developing forms, you’ll often want to update some React state when you receive user input. Or perhaps you want to perform layout in JavaScript and react to changes in some DOM element size.

In React, you would implement this by listening to a «change» event, read from your data source (usually the DOM) and call setState() on one of your components. «Closing the data flow loop» explicitly leads to more understandable and easier-to-maintain programs. See our forms documentation for more information.

LinkedStateMixin is just a thin wrapper and convention around the onChange / setState() pattern. It doesn’t fundamentally change how data flows in your React application.

LinkedStateMixin: Before and After

Here’s a simple form example without using LinkedStateMixin :

This works really well and it’s very clear how data is flowing, however, with a lot of form fields it could get a bit verbose. Let’s use LinkedStateMixin to save us some typing:

valueLink objects can be passed up and down the tree as props, so it’s easy (and explicit) to set up two-way binding between a component deep in the hierarchy and state that lives higher in the hierarchy.

Note that checkboxes have a special behavior regarding their value attribute, which is the value that will be sent on form submit if the checkbox is checked (defaults to on ). The value attribute is not updated when the checkbox is checked or unchecked. For checkboxes, you should use checkedLink instead of valueLink :

There are two sides to LinkedStateMixin : the place where you create the valueLink instance and the place where you use it. To prove how simple LinkedStateMixin is, let’s rewrite each side separately to be more explicit.

Источник

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

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