data.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. var Observer = require('../observe/observer')
  2. /**
  3. * Setup the instances data object.
  4. *
  5. * Properties are copied into the scope object to take advantage of
  6. * prototypal inheritance.
  7. *
  8. * If the `syncData` option is true, Vue will maintain property
  9. * syncing between the scope and the original data object, so that
  10. * any changes to the scope are synced back to the passed in object.
  11. * This is useful internally when e.g. creating v-repeat instances
  12. * with no alias.
  13. *
  14. * If swapping data object with the `$data` accessor, teardown
  15. * previous sync listeners and delete keys not present in new data.
  16. *
  17. * @param {Object} data
  18. * @param {Boolean} init - if not ture, indicates its a `$data` swap.
  19. */
  20. exports._initData = function (data, init) {
  21. var scope = this.$scope
  22. var options = this.$options
  23. var key
  24. if (!init) {
  25. // teardown old sync listeners
  26. if (options.syncData) {
  27. this._unsync()
  28. }
  29. // delete keys not present in the new data
  30. for (key in scope) {
  31. if (scope.hasOwnProperty(key) && !(key in data)) {
  32. scope.$delete(key)
  33. }
  34. }
  35. }
  36. // copy properties into scope
  37. for (key in data) {
  38. if (scope.hasOwnProperty(key)) {
  39. // existing property, trigger set
  40. scope[key] = data[key]
  41. } else {
  42. // new property
  43. scope.$add(key, data[key])
  44. }
  45. }
  46. // setup sync between scope and new data
  47. if (options.syncData) {
  48. this._data = data
  49. this._dataObserver = Observer.create(data)
  50. this._sync()
  51. }
  52. }
  53. /**
  54. * Setup two-way sync between the instance scope and
  55. * the original data. Requires teardown.
  56. */
  57. exports._sync = function () {
  58. var data = this._data
  59. var scope = this.$scope
  60. var locked = false
  61. var listeners = this._syncListeners = {
  62. data: {
  63. set: guard(function (key, val) {
  64. data[key] = val
  65. }),
  66. add: guard(function (key, val) {
  67. data.$add(key, val)
  68. }),
  69. delete: guard(function (key) {
  70. data.$delete(key)
  71. })
  72. },
  73. scope: {
  74. set: guard(function (key, val) {
  75. scope[key] = val
  76. }),
  77. add: guard(function (key, val) {
  78. scope.$add(key, val)
  79. }),
  80. delete: guard(function (key) {
  81. scope.$delete(key)
  82. })
  83. }
  84. }
  85. // sync scope and original data.
  86. this._observer
  87. .on('set:self', listeners.data.set)
  88. .on('add:self', listeners.data.add)
  89. .on('delete:self', listeners.data.delete)
  90. this._dataObserver
  91. .on('set:self', listeners.scope.set)
  92. .on('add:self', listeners.scope.add)
  93. .on('delete:self', listeners.scope.delete)
  94. /**
  95. * The guard function prevents infinite loop
  96. * when syncing between two observers.
  97. */
  98. function guard (fn) {
  99. return function (key, val) {
  100. if (locked) return
  101. locked = true
  102. fn(key, val)
  103. locked = false
  104. }
  105. }
  106. }
  107. /**
  108. * Teardown the sync between scope and previous data object.
  109. */
  110. exports._unsync = function () {
  111. var listeners = this._syncListeners
  112. this._observer
  113. .off('set:self', listeners.data.set)
  114. .off('add:self', listeners.data.add)
  115. .off('delete:self', listeners.data.delete)
  116. this._dataObserver
  117. .off('set:self', listeners.scope.set)
  118. .off('add:self', listeners.scope.add)
  119. .off('delete:self', listeners.scope.delete)
  120. }