Sfoglia il codice sorgente

make sure scope work properly for components inside fast-repeat

Evan You 10 anni fa
parent
commit
b9cef7c398

+ 3 - 2
src/compiler/compile-props.js

@@ -105,7 +105,7 @@ module.exports = function compileProps (el, propOptions) {
  */
 
 function makePropsLinkFn (props) {
-  return function propsLinkFn (vm, el) {
+  return function propsLinkFn (vm, scope) {
     // store resolved props info
     vm._props = {}
     var i = props.length
@@ -123,11 +123,12 @@ function makePropsLinkFn (props) {
         if (vm._context) {
           if (prop.mode === propBindingModes.ONE_TIME) {
             // one time binding
+            // TODO respect scope here
             value = vm._context.$get(prop.parentPath)
             _.initProp(vm, prop, value)
           } else {
             // dynamic binding
-            vm._bindDir('prop', el, prop, propDef)
+            vm._bindDir('prop', null, prop, propDef, null, scope)
           }
         } else {
           process.env.NODE_ENV !== 'production' && _.warn(

+ 5 - 4
src/compiler/compile.js

@@ -129,13 +129,14 @@ function teardownDirs (vm, dirs, destroying) {
  * @param {Vue} vm
  * @param {Element} el
  * @param {Object} options
+ * @param {Object} [scope]
  * @return {Function}
  */
 
-exports.compileAndLinkProps = function (vm, el, props) {
+exports.compileAndLinkProps = function (vm, el, props, scope) {
   var propsLinkFn = compileProps(el, props)
   var propDirs = linkAndCapture(function () {
-    propsLinkFn(vm, null)
+    propsLinkFn(vm, scope)
   }, vm)
   return makeUnlinkFn(vm, propDirs)
 }
@@ -180,13 +181,13 @@ exports.compileRoot = function (el, options) {
     }
   }
 
-  return function rootLinkFn (vm, el) {
+  return function rootLinkFn (vm, el, scope) {
     // link context scope dirs
     var context = vm._context
     var contextDirs
     if (context && contextLinkFn) {
       contextDirs = linkAndCapture(function () {
-        contextLinkFn(context, el)
+        contextLinkFn(context, el, null, scope)
       }, context)
     }
 

+ 9 - 1
src/directives/component.js

@@ -199,7 +199,15 @@ module.exports = {
         _linkerCachable: !this.template,
         _asComponent: true,
         _isRouterView: this._isRouterView,
-        _context: this.vm
+        // if this is a transcluded component, context
+        // will be the common parent vm of this instance
+        // and its host.
+        _context: this.vm,
+        // if this is inside an inline v-repeat, the scope
+        // will be the intermediate scope created for this
+        // repeat fragment. this is used for linking props
+        // and container directives.
+        _scope: this._scope
       }
       // extra options
       if (extraOptions) {

+ 6 - 1
src/directives/prop.js

@@ -25,7 +25,12 @@ module.exports = {
         if (_.assertProp(prop, val)) {
           child[childKey] = val
         }
-      }, { sync: true }
+      }, {
+        sync: true,
+        // important: props need to be observed on the
+        // repeat scope if present
+        scope: this._scope
+      }
     )
 
     // set the child initial value.

+ 0 - 2
src/fast-repeat.js

@@ -1,6 +1,4 @@
 var _ = require('./util')
-var compiler = require('./compiler')
-var templateParser = require('./parsers/template')
 var FragmentFactory = require('./fragment/factory')
 
 module.exports = {

+ 1 - 1
src/fragment/factory.js

@@ -44,4 +44,4 @@ FragmentFactory.prototype.create = function (host, scope) {
   return new Fragment(el, unlink)
 }
 
-module.exports  = FragmentFactory
+module.exports = FragmentFactory

+ 4 - 4
src/instance/compile.js

@@ -17,11 +17,10 @@ var compiler = require('../compiler')
 
 exports._compile = function (el) {
   var options = this.$options
-  var host = this._host
   if (options._linkFn) {
     // pre-transcluded with linker, just use it
     this._initElement(el)
-    this._unlinkFn = options._linkFn(this, el, host)
+    this._unlinkFn = options._linkFn(this, el)
   } else {
     // transclude and init element
     // transclude can potentially replace original
@@ -49,10 +48,11 @@ exports._compile = function (el) {
     }
 
     // link phase
-    var rootUnlinkFn = rootLinker(this, el)
+    // make sure to link root with prop scope!
+    var rootUnlinkFn = rootLinker(this, el, this._scope)
     var contentUnlinkFn = contentLinkFn
       ? contentLinkFn(this, el)
-      : compiler.compile(el, options)(this, el, host)
+      : compiler.compile(el, options)(this, el)
 
     // register composite unlink function
     // to be called during instance destruction

+ 12 - 6
src/instance/init.js

@@ -46,12 +46,18 @@ exports._init = function (options) {
   this._isBeingDestroyed = false
   this._unlinkFn = null
 
-  // context: the scope in which the component was used,
-  // and the scope in which props and contents of this
-  // instance should be compiled in.
-  this._context =
-    options._context ||
-    options._parent
+  // context:
+  // if this is a transcluded component, context
+  // will be the common parent vm of this instance
+  // and its host.
+  this._context = options._context || options._parent
+
+  // scope:
+  // if this is inside an inline v-repeat, the scope
+  // will be the intermediate scope created for this
+  // repeat fragment. this is used for linking props
+  // and container directives.
+  this._scope = options._scope
 
   // push self into parent / transclusion host
   if (this.$parent) {

+ 2 - 3
src/instance/scope.js

@@ -37,9 +37,8 @@ exports._initProps = function () {
   // make sure to convert string selectors into element now
   el = options.el = _.query(el)
   this._propsUnlinkFn = el && el.nodeType === 1 && props
-    ? compiler.compileAndLinkProps(
-        this, el, props
-      )
+    // props must be linked in proper scope if inside v-repeat
+    ? compiler.compileAndLinkProps(this, el, props, this._scope)
     : null
 }
 

+ 0 - 1
src/watcher.js

@@ -89,7 +89,6 @@ Watcher.prototype.get = function () {
   try {
     value = this.getter.call(scope, scope)
   } catch (e) {
-    debugger
     if (
       process.env.NODE_ENV !== 'production' &&
       config.warnExpressionErrors

+ 6 - 6
test/unit/specs/compiler/compile_spec.js

@@ -59,10 +59,10 @@ if (_.inBrowser) {
       expect(typeof linker).toBe('function')
       linker(vm, el)
       expect(vm._bindDir.calls.count()).toBe(4)
-      expect(vm._bindDir).toHaveBeenCalledWith('a', el, descriptorB, defA, undefined)
-      expect(vm._bindDir).toHaveBeenCalledWith('a', el.firstChild, descriptorA, defA, undefined)
-      expect(vm._bindDir).toHaveBeenCalledWith('b', el.firstChild, descriptorB, defB, undefined)
-      expect(vm._bindDir).toHaveBeenCalledWith('b', el.lastChild, descriptorB, defB, undefined)
+      expect(vm._bindDir).toHaveBeenCalledWith('a', el, descriptorB, defA, undefined, undefined)
+      expect(vm._bindDir).toHaveBeenCalledWith('a', el.firstChild, descriptorA, defA, undefined, undefined)
+      expect(vm._bindDir).toHaveBeenCalledWith('b', el.firstChild, descriptorB, defB, undefined, undefined)
+      expect(vm._bindDir).toHaveBeenCalledWith('b', el.lastChild, descriptorB, defB, undefined, undefined)
       // check the priority sorting
       // the "b" on the firstNode should be called first!
       expect(vm._bindDir.calls.argsFor(1)[0]).toBe('b')
@@ -115,7 +115,7 @@ if (_.inBrowser) {
       // expect 1 call because terminal should return early and let
       // the directive handle the rest.
       expect(vm._bindDir.calls.count()).toBe(1)
-      expect(vm._bindDir).toHaveBeenCalledWith('repeat', el.firstChild, descriptor, def, undefined)
+      expect(vm._bindDir).toHaveBeenCalledWith('repeat', el.firstChild, descriptor, def, undefined, undefined)
     })
 
     it('custom element components', function () {
@@ -140,7 +140,7 @@ if (_.inBrowser) {
       var linker = compile(el, Vue.options)
       linker(vm, el)
       expect(vm._bindDir.calls.count()).toBe(1)
-      expect(vm._bindDir).toHaveBeenCalledWith('attr', el.firstChild, descriptor, def)
+      expect(vm._bindDir).toHaveBeenCalledWith('attr', el.firstChild, descriptor, def, undefined, undefined)
       expect(el.firstChild.getAttribute('b')).toBe('B')
     })