Explorar o código

allow referencing host with ref inside transclusion content (close #1147)

Evan You %!s(int64=10) %!d(string=hai) anos
pai
achega
6af5db6566

+ 4 - 31
src/directives/component.js

@@ -96,8 +96,7 @@ module.exports = {
         }
       }
     }
-    var child = this.build(options)
-    this.setCurrent(child)
+    var child = this.childVM = this.build(options)
     if (!this.waitForEvent) {
       if (activateHook) {
         activateHook.call(child, insert)
@@ -142,7 +141,7 @@ module.exports = {
       // just remove current
       this.unbuild(true)
       this.remove(this.childVM, cb)
-      this.unsetCurrent()
+      this.childVM = null
     } else {
       this.resolveComponent(value, _.bind(function () {
         this.unbuild(true)
@@ -227,6 +226,7 @@ module.exports = {
         // if no inline-template, then the compiled
         // linker can be cached for better performance.
         _linkerCachable: !this.inlineTemplate,
+        _ref: this.ref,
         _asComponent: true,
         _isRouterView: this._isRouterView,
         // if this is a transcluded component, context
@@ -339,7 +339,7 @@ module.exports = {
   transition: function (target, cb) {
     var self = this
     var current = this.childVM
-    this.setCurrent(target)
+    this.childVM = target
     switch (self.transMode) {
       case 'in-out':
         target.$before(self.anchor, function () {
@@ -357,32 +357,6 @@ module.exports = {
     }
   },
 
-  /**
-   * Set childVM and parent ref
-   */
-
-  setCurrent: function (child) {
-    this.unsetCurrent()
-    this.childVM = child
-    var ref = child._refId || this.ref
-    if (ref) {
-      (this._scope || this.vm).$[ref] = child
-    }
-  },
-
-  /**
-   * Unset childVM and parent ref
-   */
-
-  unsetCurrent: function () {
-    var child = this.childVM
-    this.childVM = null
-    var ref = (child && child._refId) || this.ref
-    if (ref) {
-      (this._scope || this.vm).$[ref] = null
-    }
-  },
-
   /**
    * Unbind.
    */
@@ -391,7 +365,6 @@ module.exports = {
     this.invalidatePending()
     // Do not defer cleanup when unbinding
     this.unbuild()
-    this.unsetCurrent()
     // destroy all keep-alive cached instances
     if (this.cache) {
       for (var key in this.cache) {

+ 12 - 4
src/directives/ref.js

@@ -14,13 +14,21 @@ module.exports = {
     }
     // If we get here, it means this is a `v-ref` on a
     // child, because parent scope `v-ref` is stripped in
-    // `v-component` already. So we just record our own ref
-    // here - it will overwrite parent ref in `v-component`,
-    // if any.
-    vm._refId = this.expression
+    // `v-component` already.
+    var ref = this.expression
+    var context = this.vm._scope || this.vm._context
+    context.$[ref] = this.vm
 
     if (process.env.NODE_ENV !== 'production') {
       _.deprecation.REF_IN_CHILD()
     }
+  },
+
+  unbind: function () {
+    var ref = this.expression
+    var context = this.vm._scope || this.vm._context
+    if (context.$[ref] === this.vm) {
+      context.$[ref] = null
+    }
   }
 }

+ 5 - 0
src/instance/init.js

@@ -59,6 +59,11 @@ exports._init = function (options) {
   // and container directives.
   this._scope = options._scope
 
+  // set ref
+  if (options._ref) {
+    (this._scope || this._context).$[options._ref] = this
+  }
+
   // fragment:
   // if this instance is compiled inside a Fragment, it
   // needs to reigster itself as a child of that fragment

+ 10 - 0
src/instance/lifecycle.js

@@ -157,6 +157,14 @@ exports._destroy = function (remove, deferCleanup) {
   while (i--) {
     this._watchers[i].teardown()
   }
+  // unregister ref
+  var ref = this.$options._ref
+  if (ref) {
+    var scope = this._scope || this._context
+    if (scope.$[ref] === this) {
+      scope.$[ref] = null
+    }
+  }
   // remove reference to self on $el
   if (this.$el) {
     this.$el.__vue__ = null
@@ -197,6 +205,8 @@ exports._cleanup = function () {
   this.$root =
   this.$children =
   this._watchers =
+  this._context =
+  this._scope =
   this._directives = null
   // call the last hook...
   this._isDestroyed = true

+ 26 - 0
test/unit/specs/directives/ref_spec.js

@@ -89,6 +89,32 @@ if (_.inBrowser) {
         })
       })
     })
+  
+    // #1147
+    it('should be able to reference host via ref inside transclusion content', function (done) {
+      var vm = new Vue({
+        el: el,
+        template:
+          '<div>' +
+            '<comp ref="out">{{$.out.msg}}</comp>' +
+          '</div>',
+        components: {
+          comp: {
+            template: '<slot></slot>',
+            data: function () {
+              return { msg: 'hi' }
+            }
+          }
+        }
+      })
+      expect(_.warn).not.toHaveBeenCalled()
+      expect(vm.$el.textContent).toBe('hi')
+      vm.$children[0].msg = 'ho'
+      _.nextTick(function () {
+        expect(vm.$el.textContent).toBe('ho')
+        done()
+      })
+    })
 
     it('should also work in child template', function (done) {
       var vm = new Vue({