javascript - Što je dvosmjerno vezivanje?


Pročitao sam puno da Backbone ne radi dvosmjerno povezivanje, ali ne razumijem točno ovaj koncept.

Može li mi netko dati primjer kako dvosmjerno povezivanje funkcionira u MVC kodnoj bazi i kako ne funkcionira s Backboneom?

Odgovor #1

Dvosmjerno povezivanje jednostavno znači da:

  1. Kada se svojstva u modelu ažuriraju, tako se ažurira i korisničko sučelje.
  2. Kada se UI elementi ažuriraju, promjene se prenose natrag u model.

Backbone nema „ugrađenu“ implementaciju #2 (iako to svakako možete napraviti koristeći slušatelje događaja). Drugi okviri poput Knockouta automatski povezuju dvostruko povezivanje.


U Backboneu, lako možete postići #1 vezanjem metode "render" prikaza na "change" događaj njegovog modela. Za postizanje #2, također trebate dodati slušatelja promjena na ulazni element i pozvati model.set u rukovatelju.

Evo Fiddle s postavkama dvosmjernog povezivanja u Backboneu.

Odgovor #2

Dvostrano vezanje znači da se sve promjene vezane uz podatke koje utječu na model odmah prenose na odgovarajući prikaz (ili prikaze), i da se sve promjene napravljene u prikazu (na primjer, od strane korisnika) odmah odražavaju u temeljnome modelu. Kada se podaci aplikacije promijene, promijeni se i korisničko sučelje, i obrnuto.

Ovo je vrlo solidan koncept za izgradnju web aplikacije na njemu, jer čini apstrakciju „Model“ sigurnim, atomarnim izvorom podataka koji se može koristiti svugdje unutar aplikacije. Recimo, ako se model, vezan uz prikaz, promijeni, tada će njegov odgovarajući dio korisničkog sučelja (prikaz) to odraziti, bez obzira na sve. I odgovarajući dio korisničkog sučelja (prikaz) može sigurno služiti kao sredstvo za prikupljanje korisničkih unosa/podataka, kako bi se podaci u aplikaciji održali ažurnima.

Dobra implementacija dvosmjernog povezivanja očito bi trebala učiniti ovu vezu između modela i nekog prikaza/prikaza što jednostavnijom, s gledišta programera.

Tada je zapravo prilično netočno reći da Backbone ne podržava dvosmjerno povezivanje: iako nije osnovna značajka okvira, može se prilično jednostavno izvršiti koristeći Backbone-ove događaje. Za jednostavne slučajeve to zahtijeva nekoliko eksplicitnih redaka koda; a za složenija povezivanja može postati prilično rizično. Evo jednostavnog slučaja (netestirani kod, napisan u hodu samo za ilustraciju):

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}

Ovo je prilično tipičan obrazac u sirovoj Backbone aplikaciji. Kao što se može vidjeti, zahtijeva solidnu količinu (prilično standardnog) koda.

AngularJS i neke druge alternative (Ember, Knockout…) pružaju dvosmjerno vezanje kao primarnu funkcionalnost. One apstrahiraju mnoge rubne slučajeve pod neki DSL i čine najbolje što mogu pri integraciji dvosmjernog vezanja unutar svog ekosustava. Naš primjer bi s AngularJS izgledao otprilike ovako (netestirani kod, vidi gore):

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

Prilično kratko!

Ali, imajte na umu da neke potpune dvosmjerne veze doista postoje i za Backbone (u sirovom, subjektivnom redu prema opadajućoj složenosti): Epoxy, Stickit, ModelBinder…

Jedna zgodna stvar s Epoxyjem, na primjer, jest što vam omogućuje da deklarirate svoje povezivanja (atribute modela <-> DOM element prikaza) ili unutar predloška (DOM), ili unutar implementacije prikaza (JavaScript). Neki ljudi vrlo ne vole dodavanje "direktiva" u DOM/predložak (poput ng-* atributa potrebnih za AngularJS, ili data-bind atributa u Emberu).

Uzimajući Epoxy kao primjer, može se preraditi osnovna Backbone aplikacija u nešto poput ovoga (…):

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}

Sve u svemu, prilično svi "mainstream" JS okviri podržavaju dvosmjerno povezivanje. Neki od njih, poput Backbone-a, zahtijevaju dodatni rad da bi to funkcioniralo glatko, ali to su isti okviri koji uopće ne nameću poseban način kako to raditi. Dakle, zapravo je riječ o vašem stanju duha.

Također, možda će vas zanimati Flux, drugačija arhitektura web aplikacija koja promiče jednosmjerno vezanje kroz kružni obrazac. Temelji se na konceptu brzog, cjelovitog ponovno prikazivanja UI komponenti pri bilo kojoj promjeni podataka kako bi se osigurala kohezivnost i olakšalo razmišljanje o kodu/toku podataka. U istom trendu, možda ćete htjeti provjeriti koncept MVI (Model-View-Intent), na primjer Cycle.

Odgovor #3

McGarnagle ima sjajan odgovor, i željet ćete prihvatiti njegov, ali pomislio sam da bih spomenuo (budući da ste pitali) kako databinding funkcionira.

Općenito se provodi slanjem događaja kad god se izvrši promjena podataka, što potom uzrokuje ažuriranje slušatelja (npr. korisničkog sučelja).

Dvosmjerno vezivanje radi tako da se ovo radi dvaput, uz malo pažnje kako bi se osiguralo da ne završiš zaglavljen u petlji događaja (gdje ažuriranje iz događaja uzrokuje pokretanje drugog događaja).

Mislio sam staviti ovo u komentar, ali je postajalo prilično dugo...

Povezani članci