| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- var Observer = require('../observe/observer')
- /**
- * Setup the instances data object.
- *
- * Properties are copied into the scope object to take advantage of
- * prototypal inheritance.
- *
- * If the `syncData` option is true, Vue will maintain property
- * syncing between the scope and the original data object, so that
- * any changes to the scope are synced back to the passed in object.
- * This is useful internally when e.g. creating v-repeat instances
- * with no alias.
- *
- * If swapping data object with the `$data` accessor, teardown
- * previous sync listeners and delete keys not present in new data.
- *
- * @param {Object} data
- * @param {Boolean} init - if not ture, indicates its a `$data` swap.
- */
- exports._initData = function (data, init) {
- var scope = this.$scope
- var options = this.$options
- var key
- if (!init) {
- // teardown old sync listeners
- if (options.syncData) {
- this._unsync()
- }
- // delete keys not present in the new data
- for (key in scope) {
- if (scope.hasOwnProperty(key) && !(key in data)) {
- scope.$delete(key)
- }
- }
- }
- // copy properties into scope
- for (key in data) {
- if (scope.hasOwnProperty(key)) {
- // existing property, trigger set
- scope[key] = data[key]
- } else {
- // new property
- scope.$add(key, data[key])
- }
- }
- // setup sync between scope and new data
- if (options.syncData) {
- this._data = data
- this._dataObserver = Observer.create(data)
- this._sync()
- }
- }
- /**
- * Setup two-way sync between the instance scope and
- * the original data. Requires teardown.
- */
- exports._sync = function () {
- var data = this._data
- var scope = this.$scope
- var locked = false
- var listeners = this._syncListeners = {
- data: {
- set: guard(function (key, val) {
- data[key] = val
- }),
- add: guard(function (key, val) {
- data.$add(key, val)
- }),
- delete: guard(function (key) {
- data.$delete(key)
- })
- },
- scope: {
- set: guard(function (key, val) {
- scope[key] = val
- }),
- add: guard(function (key, val) {
- scope.$add(key, val)
- }),
- delete: guard(function (key) {
- scope.$delete(key)
- })
- }
- }
- // sync scope and original data.
- this._observer
- .on('set:self', listeners.data.set)
- .on('add:self', listeners.data.add)
- .on('delete:self', listeners.data.delete)
- this._dataObserver
- .on('set:self', listeners.scope.set)
- .on('add:self', listeners.scope.add)
- .on('delete:self', listeners.scope.delete)
- /**
- * The guard function prevents infinite loop
- * when syncing between two observers.
- */
- function guard (fn) {
- return function (key, val) {
- if (locked) return
- locked = true
- fn(key, val)
- locked = false
- }
- }
- }
- /**
- * Teardown the sync between scope and previous data object.
- */
- exports._unsync = function () {
- var listeners = this._syncListeners
- this._observer
- .off('set:self', listeners.data.set)
- .off('add:self', listeners.data.add)
- .off('delete:self', listeners.data.delete)
- this._dataObserver
- .off('set:self', listeners.scope.set)
- .off('add:self', listeners.scope.add)
- .off('delete:self', listeners.scope.delete)
- }
|