Что такое двухстороннее связывание?


173

Я много читал, что Backbone не выполняет двустороннюю привязку, но я не совсем понимаю эту концепцию.

Может ли кто-нибудь дать мне пример того, как двухстороннее связывание работает в базе кода MVC и как это не работает с Backbone?

Ответы:


249

Двухстороннее связывание означает, что:

  1. Когда свойства в модели обновляются, то же происходит и с пользовательским интерфейсом.
  2. Когда элементы пользовательского интерфейса обновляются, изменения распространяются обратно в модель.

Backbone не имеет встроенной реализации # 2 (хотя вы, безусловно, можете сделать это с помощью прослушивателей событий). Другие фреймворки, такие как Knockout, автоматически выполняют двустороннее связывание .


В Backbone вы можете легко достичь # 1, привязав метод представления "render" к событию "change" его модели. Чтобы достичь # 2, вам также необходимо добавить прослушиватель изменений к элементу ввода и вызвать model.setобработчик.

Вот Скрипка с двусторонней привязкой, настроенная в Backbone.


25
Ответ так болезненно очевиден, когда вы его видите. Большое спасибо за то, что нашли время дать четкий ответ и пример.
Крис М

А с Firebase поставляется ... 3-х стороннее связывание данных -> Вид, Модель, База данных. Просто подумал, что это было довольно аккуратно.
Леви Фуллер

Краткий и короткий. +1
Karan_powered_by_RedBull

46

Двухстороннее связывание означает, что любые связанные с данными изменения, влияющие на модель, немедленно распространяются на соответствующие представления и что любые изменения, сделанные в представлениях (скажем, пользователем), немедленно отражаются в базовой модели. , Когда изменяются данные приложения, меняется и пользовательский интерфейс, и наоборот.

Это очень надежная концепция для построения веб-приложения на основе, поскольку она делает абстракцию «Модель» безопасным атомарным источником данных, который можно использовать повсюду в приложении. Скажем, если модель, привязанная к представлению, изменится, то соответствующий элемент пользовательского интерфейса (представление) отразит это, несмотря ни на что . И соответствующий элемент пользовательского интерфейса (представление) можно безопасно использовать в качестве средства сбора пользовательских данных / данных, чтобы поддерживать данные приложения в актуальном состоянии.

Хорошая реализация двустороннего связывания, очевидно, должна сделать эту связь между моделью и некоторыми представлениями как можно более простой, с точки зрения разработчика.

Тогда совершенно неверно утверждать, что Backbone не поддерживает двустороннее связывание: хотя это и не является основной функцией среды, ее можно выполнить довольно просто, используя События Backbone. Для простых случаев стоит несколько явных строк кода; и может стать довольно опасным для более сложных привязок. Вот простой случай (непроверенный код, написанный на лету только для иллюстрации):

Model = Backbone.Model.extend
  defaults:
    data: ''

View = Backbone.View.extend
  template: _.template("Edit the data: <input type='text' value='<%= data %>' />")

  events:
    # Listen for user inputs, and edit the model.
    'change input': @setData

  initialize: (options) ->
    # Listen for model's edition, and trigger UI update
    @listenTo @model, 'change:data', @render

  render: ->
    @$el.html @template(@model.attributes)
    @

  setData: (e) =>
    e.preventDefault()
    @model.set 'data', $(e.currentTarget).value()

model: new Model()
view = new View {el: $('.someEl'), model: model}

Это довольно типичный шаблон в необработанном приложении Backbone. Как видите, для этого требуется приличное количество (довольно стандартного) кода.

AngularJS и некоторые другие альтернативы ( Ember , Knockout ...) обеспечивают двустороннюю привязку в качестве функции от первого лица. Они абстрагируют многие крайние случаи в рамках некоторых DSL и делают все возможное для интеграции двухстороннего связывания в свою экосистему. Наш пример будет выглядеть примерно так с AngularJS (непроверенный код, см. Выше):

<div ng-app="app" ng-controller="MainCtrl">
  Edit the data:
  <input name="mymodel.data" ng-model="mymodel.data">
</div>
angular.module('app', [])
  .controller 'MainCtrl', ($scope) ->
    $scope.mymodel = {data: ''}

Довольно короткий!

Но имейте в виду, что некоторые полноценные расширения двустороннего связывания существуют и для Backbone (в необработанном субъективном порядке уменьшения сложности): Epoxy , Stickit , ModelBinder

Например, в Epoxy есть одна интересная особенность: она позволяет вам объявлять ваши привязки (атрибуты модели <-> элемента DOM представления) либо внутри шаблона (DOM), либо внутри реализации представления (JavaScript). Некоторые люди сильно не любят добавлять «директивы» в DOM / шаблон (такие как атрибуты ng- *, требуемые AngularJS, или атрибуты привязки данных Ember).

Взяв в качестве примера Epoxy, можно преобразовать необработанное приложение Backbone во что-то вроде этого (…):

Model = Backbone.Model.extend
  defaults:
    data: ''

View = Backbone.Epoxy.View.extend
  template: _.template("Edit the data: <input type='text' />")
  # or, using the inline form: <input type='text' data-bind='value:data' />

  bindings:
    'input': 'value:data'

  render: ->
    @$el.html @template(@model.attributes)
    @

model: new Model()
view = new View {el: $('.someEl'), model: model}

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

Также вас может заинтересовать Flux , другая архитектура для веб-приложений, поддерживающая одностороннее связывание по круговой схеме. Он основан на концепции быстрой и целостной визуализации компонентов пользовательского интерфейса при любых изменениях данных для обеспечения согласованности и упрощения рассуждений о коде / потоке данных. В той же тенденции вы можете проверить концепцию MVI (Model-View-Intent), например, Cycle .


3
Многие разработчики, особенно разработчики React / Flux, не считают двустороннее связывание безопасным шаблоном для создания крупномасштабных приложений.
Энди

28

У McGarnagle есть отличный ответ, и вы захотите принять его, но я подумал, что упомяну (так как вы спросили), как работает привязка данных.

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

Двухстороннее связывание работает, делая это дважды, с некоторой осторожностью, чтобы гарантировать, что вы не застряли в цикле событий (когда обновление события вызывает другое событие, которое будет запущено).

Я собирался поместить это в комментарий, но это становилось довольно длинным ...


2

На самом деле emberjsподдерживает двустороннюю привязку, которая является одной из самых мощных функций для инфраструктуры MVC javascript. Вы можете проверить это, где это упоминает bindingв его руководстве пользователя.

для emberjs создать двухстороннее связывание, создав новое свойство со строкой Binding в конце, а затем указав путь из глобальной области видимости:

App.wife = Ember.Object.create({
  householdIncome: 80000
});

App.husband = Ember.Object.create({
  householdIncomeBinding: 'App.wife.householdIncome'
});

App.husband.get('householdIncome'); // 80000

// Someone gets raise.
App.husband.set('householdIncome', 90000);
App.wife.get('householdIncome'); // 90000

Обратите внимание, что привязки не обновляются сразу. Ember ожидает завершения всего кода вашего приложения, прежде чем синхронизировать изменения, поэтому вы можете изменять связанное свойство столько раз, сколько захотите, не беспокоясь о накладных расходах при синхронизации привязок, когда значения временные.

Надеюсь, это поможет в расширении оригинального ответа.


1

Стоит упомянуть, что есть много разных решений, которые предлагают двухстороннее связывание и играют действительно хорошо.

У меня был приятный опыт работы с этой моделью подшивки - https://github.com/theironcook/Backbone.ModelBinder . который дает разумные значения по умолчанию, и в то же время множество пользовательских селекторных сопоставлений атрибутов модели с входными элементами.

На github есть более расширенный список расширений / плагинов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.