Repository: vuejs/vue
Pull request: fix $refs updating

ref: #3204


Changed files:
filename: src/compiler/compile.js
patch: @@ -665,8 +665,8 @@ function makeTerminalNodeLinkFn (el, dirName, value, options, def, rawName, arg,
     modifiers: modifiers,
     def: def
   }
-  // check ref for v-for and router-view
-  if (dirName === 'for' || dirName === 'router-view') {
+  // check ref for v-for, v-if and router-view
+  if (dirName === 'for' || dirName === 'if' || dirName === 'router-view') {
     descriptor.ref = findRef(el)
   }
   var fn = function terminalNodeLinkFn (vm, el, host, scope, frag) {

filename: src/directives/public/for.js
patch: @@ -16,7 +16,8 @@ import {
   def,
   cancellable,
   isArray,
-  isPlainObject
+  isPlainObject,
+  findVmFromFrag
 } from '../../util/index'
 
 let uid = 0
@@ -602,24 +603,6 @@ function findPrevFrag (frag, anchor, id) {
   return frag
 }
 
-/**
- * Find a vm from a fragment.
- *
- * @param {Fragment} frag
- * @return {Vue|undefined}
- */
-
-function findVmFromFrag (frag) {
-  let node = frag.node
-  // handle multi-node frag
-  if (frag.end) {
-    while (!node.__vue__ && node !== frag.end && node.nextSibling) {
-      node = node.nextSibling
-    }
-  }
-  return node.__vue__
-}
-
 /**
  * Create a range array from given number.
  *

filename: src/directives/public/if.js
patch: @@ -5,7 +5,8 @@ import {
   remove,
   replace,
   createAnchor,
-  warn
+  warn,
+  findVmFromFrag
 } from '../../util/index'
 
 export default {
@@ -40,8 +41,10 @@ export default {
     if (value) {
       if (!this.frag) {
         this.insert()
+        this.updateRef(value)
       }
     } else {
+      this.updateRef(value)
       this.remove()
     }
   },
@@ -76,6 +79,29 @@ export default {
     }
   },
 
+  updateRef (value) {
+    var ref = this.descriptor.ref
+    if (!ref) return
+    var hash = (this.vm || this._scope).$refs
+    var refs = hash[ref]
+    var key = this._frag.scope.$key
+    if (!refs) return
+    if (value) {
+      if (Array.isArray(refs)) {
+        refs.push(findVmFromFrag(this._frag))
+      } else {
+        refs[key] = findVmFromFrag(this._frag)
+      }
+    } else {
+      if (Array.isArray(refs)) {
+        refs.$remove(findVmFromFrag(this._frag))
+      } else {
+        refs[key] = null
+        delete refs[key]
+      }
+    }
+  },
+
   unbind () {
     if (this.frag) {
       this.frag.destroy()

filename: src/util/dom.js
patch: @@ -449,3 +449,21 @@ export function getOuterHTML (el) {
     return container.innerHTML
   }
 }
+
+/**
+ * Find a vm from a fragment.
+ *
+ * @param {Fragment} frag
+ * @return {Vue|undefined}
+ */
+
+export function findVmFromFrag (frag) {
+  let node = frag.node
+  // handle multi-node frag
+  if (frag.end) {
+    while (!node.__vue__ && node !== frag.end && node.nextSibling) {
+      node = node.nextSibling
+    }
+  }
+  return node.__vue__
+}

filename: test/unit/specs/directives/public/if_spec.js
patch: @@ -432,4 +432,77 @@ describe('v-if', function () {
       done()
     })
   })
+
+  // GitHub issue #3204
+  it('update array refs', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<foo v-if="!activeItem || $index === activeItem" v-ref:foo :index="$index" v-for="item in items"></foo>',
+      data: {
+        items: [0, 1, 2],
+        activeItem: null
+      },
+      components: {
+        foo: {
+          props: ['index'],
+          template: '<div>I am foo ({{ index }})<div>'
+        }
+      }
+    })
+    vm.$refs.foo.forEach(function (ref, index) {
+      expect(ref.$el.textContent).toBe('I am foo (' + index + ')')
+      expect(ref.index).toBe(index)
+    })
+    vm.activeItem = 1 // select active item
+    nextTick(function () {
+      expect(vm.$refs.foo.length).toBe(1)
+      expect(vm.$refs.foo[0].index).toBe(1)
+      vm.activeItem = null // enable all elements
+      nextTick(function () {
+        expect(vm.$refs.foo.length).toBe(3)
+        done()
+      })
+    })
+  })
+
+  it('update object refs', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<foo v-if="!activeKey || $key === activeKey" v-ref:foo :key="$key" v-for="item in items"></foo>',
+      data: {
+        items: {
+          a: 1,
+          b: 2,
+          c: 3
+        },
+        activeKey: null
+      },
+      components: {
+        foo: {
+          props: ['key'],
+          template: '<div>I am foo ({{ key }})<div>'
+        }
+      }
+    })
+    Object.keys(vm.$refs.foo).forEach(function (key) {
+      var ref = vm.$refs.foo[key]
+      expect(ref.$el.textContent).toBe('I am foo (' + key + ')')
+      expect(ref.key).toBe(key)
+    })
+    vm.activeKey = 'b' // select active item
+    nextTick(function () {
+      expect(Object.keys(vm.$refs.foo).length).toBe(1)
+      expect(vm.$refs.foo['b'].key).toBe('b')
+      vm.activeKey = null // enable all elements
+      nextTick(function () {
+        expect(Object.keys(vm.$refs.foo).length).toBe(3)
+        Object.keys(vm.$refs.foo).forEach(function (key) {
+          var ref = vm.$refs.foo[key]
+          expect(ref.$el.textContent).toBe('I am foo (' + key + ')')
+          expect(ref.key).toBe(key)
+        })
+        done()
+      })
+    })
+  })
 })