Co to jest dwustronne wiązanie?

173

Czytałem wiele, że Backbone nie wykonuje dwustronnego wiązania, ale nie do końca rozumiem tę koncepcję.

Czy ktoś mógłby mi podać przykład, jak działa dwukierunkowe wiązanie w bazie kodu MVC, a jak nie działa w przypadku Backbone?

Chris M.
źródło

Odpowiedzi:

249

Dwukierunkowe wiązanie oznacza po prostu, że:

  1. Gdy właściwości w modelu są aktualizowane, tak samo dzieje się z interfejsem użytkownika.
  2. Kiedy elementy interfejsu użytkownika są aktualizowane, zmiany są propagowane z powrotem do modelu.

Backbone nie ma wbudowanej implementacji # 2 (chociaż z pewnością można to zrobić używając detektorów zdarzeń). Inne frameworki, takie jak Knockout, automagicznie łączą dwukierunkowe powiązanie .


W Backbone możesz łatwo osiągnąć # 1, wiążąc metodę „render” widoku ze zdarzeniem „change” jego modelu. Aby osiągnąć # 2, musisz również dodać odbiornik zmian do elementu wejściowego i wywołać model.setprocedurę obsługi.

Oto Fiddle z dwukierunkowym wiązaniem ustawionym w Backbone.

McGarnagle
źródło
25
Odpowiedź jest tak boleśnie oczywista, gdy ją zobaczysz. Bardzo dziękuję za poświęcenie czasu na udzielenie jasnej odpowiedzi i przykładu.
Chris M
A wraz z Firebase jest ... 3-drożne wiązanie danych -> Widok, Model, Baza danych. Pomyślałem, że to całkiem fajne.
Levi Fuller
Zwięzłe i krótkie. +1
Karan_powered_by_RedBull
46

Wiązanie dwukierunkowe oznacza, że ​​wszelkie zmiany związane z danymi wpływające na model są natychmiast propagowane do pasujących widoków, a wszelkie zmiany wprowadzone w widokach (powiedzmy przez użytkownika) są natychmiast odzwierciedlane w modelu bazowym . Kiedy zmieniają się dane aplikacji, zmienia się również interfejs użytkownika i na odwrót.

Jest to bardzo solidna koncepcja tworzenia aplikacji internetowych, ponieważ sprawia, że ​​abstrakcja „Modelu” jest bezpiecznym, atomowym źródłem danych, które można wykorzystać wszędzie w aplikacji. Powiedzmy, że jeśli model powiązany z widokiem ulegnie zmianie, to odpowiadający mu element interfejsu użytkownika (widok) będzie to odzwierciedlał, bez względu na wszystko . A pasujący element interfejsu użytkownika (widok) może być bezpiecznie używany jako środek do gromadzenia danych wejściowych / danych użytkownika, aby zapewnić aktualność danych aplikacji.

Dobra dwukierunkowa wiążąca implementacja powinna oczywiście uczynić to połączenie między modelem a niektórymi widokami tak prostymi, jak to tylko możliwe, z punktu widzenia programisty.

Nie jest zatem prawdą stwierdzenie, że Backbone nie obsługuje dwukierunkowego wiązania: chociaż nie jest to podstawowa funkcja frameworka, można to jednak wykonać po prostu przy użyciu zdarzeń Backbone. W prostych przypadkach kosztuje to kilka wyraźnych linii kodu; i może stać się dość niebezpieczne w przypadku bardziej złożonych wiązań. Oto prosty przypadek (nieprzetestowany kod, napisany w locie tylko dla ilustracji):

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}

Jest to dość typowy wzór w surowej aplikacji Backbone. Jak widać, wymaga przyzwoitej ilości (dość standardowego) kodu.

AngularJS i kilka innych alternatyw ( Ember , Knockout …) zapewnia dwustronne wiązanie jako funkcja pierwszego obywatela. Abstrahują wiele przypadków skrajnych w ramach niektórych DSL i robią wszystko, co w ich mocy, aby zintegrować dwustronne wiązanie w swoim ekosystemie. Nasz przykład wyglądałby mniej więcej tak z AngularJS (nieprzetestowany kod, patrz wyżej):

<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: ''}

Raczej krótki!

Ale należy pamiętać, że niektóre pełnoprawnego rozszerzenia wiążące dwupasmowe istnieją dla Backbone jak również (w surowym, subiektywnego ze zmniejszającym złożoność): epoksydowa , Stickit , ModelBinder ...

Jedną fajną rzeczą w przypadku Epoxy jest na przykład to, że umożliwia on deklarowanie powiązań (atrybutów modelu <-> elementu DOM widoku) w szablonie (DOM) lub w implementacji widoku (JavaScript). Niektórzy ludzie bardzo nie lubią dodawać „dyrektyw” do DOM / szablonu (takich jak atrybuty ng- * wymagane przez AngularJS lub atrybuty wiązania danych w Ember).

Biorąc jako przykład Epoxy, można przerobić surową aplikację Backbone na coś takiego (…):

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}

Podsumowując, prawie wszystkie "mainstreamowe" frameworki JS obsługują dwukierunkowe wiązanie. Niektóre z nich, takie jak Backbone, wymagają dodatkowej pracy, aby działał płynnie , ale są to te same, które nie narzucają określonego sposobu, aby to zrobić. Więc tak naprawdę chodzi o twój stan umysłu.

Możesz również być zainteresowany Flux , inną architekturą dla aplikacji internetowych, która promuje jednokierunkowe wiązanie za pomocą kołowego wzorca. Opiera się na koncepcji szybkiego, holistycznego ponownego renderowania komponentów interfejsu użytkownika po każdej zmianie danych, aby zapewnić spójność i ułatwić wnioskowanie o kodzie / przepływie danych. W tym samym trendzie możesz chcieć sprawdzić koncepcję MVI (Model-View-Intent), na przykład Cycle .

chikamichi
źródło
3
Wielu programistów, zwłaszcza deweloperów React / Flux, nie bierze pod uwagę dwukierunkowego wiązania bezpiecznego wzorca do tworzenia aplikacji na dużą skalę.
Andy
28

McGarnagle ma świetną odpowiedź i będziesz chciał zaakceptować jego, ale pomyślałem, że wspomnę (skoro zapytałeś), jak działa wiązanie danych.

Zwykle jest to realizowane przez uruchamianie zdarzeń za każdym razem, gdy wprowadzana jest zmiana w danych, która następnie powoduje aktualizację odbiorników (np. Interfejsu użytkownika).

Wiązanie dwukierunkowe działa w ten sposób, że robi się to dwukrotnie, z pewną ostrożnością, aby upewnić się, że nie utkniesz w pętli zdarzeń (gdzie aktualizacja ze zdarzenia powoduje uruchomienie innego zdarzenia).

Miałem to zamieścić w komentarzu, ale trwało to dość długo ...

Jeff
źródło
2

Właściwie emberjsobsługuje dwukierunkowe wiązanie, które jest jedną z najpotężniejszych funkcji dla frameworka javascript MVC. Możesz to sprawdzić bindingw swoim przewodniku użytkownika.

w przypadku emberjs utworzenie dwukierunkowego wiązania polega na utworzeniu nowej właściwości z ciągiem Binding na końcu, a następnie określeniu ścieżki z zakresu globalnego:

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

Zwróć uwagę, że powiązania nie aktualizują się natychmiast. Ember czeka, aż cały kod aplikacji zakończy działanie, zanim zsynchronizujesz zmiany, dzięki czemu możesz zmieniać powiązaną właściwość tyle razy, ile chcesz, nie martwiąc się o obciążenie synchronizacji powiązań, gdy wartości są przejściowe.

Mam nadzieję, że pomoże to w przedłużeniu wybranej pierwotnej odpowiedzi.

projekt weia
źródło
1

Warto wspomnieć, że istnieje wiele różnych rozwiązań, które oferują wiązanie dwukierunkowe i naprawdę fajną grę.

Miałem przyjemne doświadczenie z tym modelem segregatora - https://github.com/theironcook/Backbone.ModelBinder . co daje rozsądne wartości domyślne, ale wiele niestandardowych mapowań selektorów jquery atrybutów modelu na elementy wejściowe.

Jest bardziej rozszerzona lista rozszerzeń centralna / wtyczek na github

Daniel
źródło