bindings.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. var Binding = require('../binding')
  2. var Path = require('../parse/path')
  3. var Observer = require('../observe/observer')
  4. /**
  5. * Setup the binding tree.
  6. *
  7. * Bindings form a tree-like structure that maps the Object
  8. * structure of observed data. However, only paths present
  9. * in the templates are created in the binding tree. When a
  10. * change event from the data observer arrives on the
  11. * instance, we traverse the binding tree along the changed
  12. * path to find the corresponding binding, and trigger
  13. * change for all its subscribers.
  14. */
  15. exports._initBindings = function () {
  16. var root = this._rootBinding = new Binding()
  17. // the $data binding points to the root itself!
  18. root._addChild('$data', root)
  19. // point $parent and $root bindings to their
  20. // repective owners.
  21. if (this.$parent) {
  22. root._addChild('$parent', this.$parent._rootBinding)
  23. root._addChild('$root', this.$root._rootBinding)
  24. }
  25. // setup observer events
  26. this.$observer
  27. // simple updates
  28. .on('set', this._updateBindingAt)
  29. .on('mutate', this._updateBindingAt)
  30. .on('delete', this._updateBindingAt)
  31. // adding properties is a bit different
  32. .on('add', this._updateAdd)
  33. // collect dependency
  34. .on('get', this._collectDep)
  35. }
  36. /**
  37. * Retrive a binding at a given path.
  38. * If `create` is true, create all bindings that do not
  39. * exist yet along the way.
  40. *
  41. * @param {String} path
  42. * @return {Binding|undefined}
  43. */
  44. exports._getBindingAt = function (path) {
  45. return Path.getFromObserver(this._rootBinding, path)
  46. }
  47. /**
  48. * Create a binding at a given path. Will also create
  49. * all bindings that do not exist yet along the way.
  50. *
  51. * @param {String} path
  52. * @return {Binding}
  53. */
  54. exports._createBindingAt = function (path) {
  55. path = path.split(Observer.pathDelimiter)
  56. var b = this._rootBinding
  57. var child, key
  58. for (var i = 0, l = path.length; i < l; i++) {
  59. key = path[i]
  60. child = b[key] || b._addChild(key)
  61. b = child
  62. }
  63. return b
  64. }
  65. /**
  66. * Trigger update for the binding at given path.
  67. *
  68. * @param {String} path
  69. * @param {String} k - unused
  70. * @param {*} v - unused
  71. * @param {Boolean} fromScope
  72. */
  73. exports._updateBindingAt = function (path, k, v, fromScope) {
  74. // root binding updates on any change,
  75. // but only if the change is not from parent scopes
  76. if (!fromScope) {
  77. this._rootBinding._notify()
  78. }
  79. var binding = this._getBindingAt(path, true)
  80. if (binding) {
  81. binding._notify()
  82. }
  83. }
  84. /**
  85. * For newly added properties, since its binding has not
  86. * been created yet, directives will not have it as a
  87. * dependency yet. However, they will have its parent as a
  88. * dependency. Therefore here we remove the last segment
  89. * from the path and notify the added property's parent
  90. * instead.
  91. *
  92. * @param {String} path
  93. */
  94. exports._updateAdd = function (path) {
  95. var index = path.lastIndexOf(Observer.pathDelimiter)
  96. if (index > -1) path = path.slice(0, index)
  97. this._updateBindingAt(path)
  98. }
  99. /**
  100. * Collect dependency for the target directive being
  101. * evaluated.
  102. *
  103. * @param {String} path
  104. */
  105. exports._collectDep = function (path) {
  106. var watcher = this._activeWatcher
  107. // the get event might have come from a child vm's watcher
  108. // so this._activeWatcher is not guarunteed to be defined
  109. if (watcher) {
  110. watcher.addDep(path)
  111. }
  112. }