Procházet zdrojové kódy

component wait-for & transition-mode

Evan You před 11 roky
rodič
revize
2eee405cc1

+ 0 - 1
src/api/lifecycle.js

@@ -103,7 +103,6 @@ exports.$destroy = function (remove, cb) {
     })
   } else {
     cleanup(self)
-    if (cb) cb()
   }
 }
 

+ 15 - 0
src/directive.js

@@ -153,6 +153,21 @@ p._checkStatement = function () {
   }
 }
 
+/**
+ * Check for an attribute directive param, e.g. lazy
+ *
+ * @param {String} name
+ * @return {String}
+ */
+
+p._checkParam = function (name) {
+  var param = this.el.getAttribute(name)
+  if (param !== null) {
+    this.el.removeAttribute(name)
+  }
+  return param
+}
+
 /**
  * Teardown the watcher and call unbind.
  */

+ 8 - 4
src/directives/component.js

@@ -35,8 +35,9 @@ module.exports = {
         this.childVM = this.build()
         this.childVM.$before(this.ref)
       } else {
-        this.readyEvent = this.el.getAttribute('ready-event')
-        this.transMode = this.el.getAttribute('transition-mode')
+        // check dynamic component params
+        this.readyEvent = this._checkParam('wait-for')
+        this.transMode = this._checkParam('transition-mode')
       }
     } else {
       _.warn(
@@ -147,7 +148,7 @@ module.exports = {
       var child = this.build()
       var self = this
       if (this.readyEvent) {
-        child.$on(this.readyEvent, function () {
+        child.$once(this.readyEvent, function () {
           self.swapTo(child)
         })
       } else {
@@ -169,18 +170,20 @@ module.exports = {
       case 'in-out':
         child.$before(self.ref, function () {
           self.unbuild(true)
+          self.childVM = child
         })
         break
       case 'out-in':
         self.unbuild(true, function () {
           child.$before(self.ref)
+          self.childVM = child
         })
         break
       default:
         self.unbuild(true)
         child.$before(self.ref)
+        self.childVM = child
     }
-    self.childVM = child
   },
 
   /**
@@ -201,6 +204,7 @@ module.exports = {
         }
         child.$destroy()
       }
+      this.cache = null
     }
   }
 

+ 1 - 2
src/directives/model/select.js

@@ -7,9 +7,8 @@ module.exports = {
     var self = this
     var el = this.el
     // check options param
-    var optionsParam = el.getAttribute('options')
+    var optionsParam = this._checkParam('options')
     if (optionsParam) {
-      el.removeAttribute('options')
       initOptions.call(this, optionsParam)
     }
     this.multiple = el.hasAttribute('multiple')

+ 2 - 14
src/directives/repeat.js

@@ -40,8 +40,9 @@ module.exports = {
     // at v-repeat level
     this.checkIf()
     this.checkRef()
-    this.checkTrackById()
     this.checkComponent()
+    // check for trackby param
+    this.idKey = this._checkParam('trackby')
     // cache for primitive value instances
     this.cache = Object.create(null)
   },
@@ -74,19 +75,6 @@ module.exports = {
       : null
   },
 
-  /**
-   * Check for a track-by ID, which allows us to identify
-   * a piece of data and its associated instance by its
-   * unique id.
-   */
-
-  checkTrackById: function () {
-    this.idKey = this.el.getAttribute('trackby')
-    if (this.idKey !== null) {
-      this.el.removeAttribute('trackby')
-    }
-  },
-
   /**
    * Check the component constructor to use for repeated
    * instances. If static we resolve it now, otherwise it

+ 147 - 9
test/unit/specs/directives/component_spec.js

@@ -86,7 +86,11 @@ if (_.inBrowser) {
       vm.view = 'b'
       _.nextTick(function () {
         expect(el.innerHTML).toBe('<div view="b">BBB</div><!--v-component-->')
-        done()
+        vm.view = ''
+        _.nextTick(function () {
+          expect(el.innerHTML).toBe('<!--v-component-->')
+          done()
+        })
       })
     })
 
@@ -195,19 +199,153 @@ if (_.inBrowser) {
       })
     })
 
-    it('teardown', function () {
+    it('wait-for', function (done) {
+      var vm = new Vue({
+        el: el,
+        data: {
+          view: 'a'
+        },
+        template: '<div v-component="{{view}}" wait-for="ok"></div>',
+        components: {
+          a: {
+            template: 'AAA'
+          },
+          b: {
+            template: 'BBB'
+          }
+        }
+      })
+      vm._children[0].$emit('ok')
+      vm.view = 'b'
+      _.nextTick(function () {
+        expect(el.textContent).toBe('AAA')
+        vm._children[1].$emit('ok')
+        expect(el.textContent).toBe('BBB')
+        done()
+      })
+    })
+
+    it('transition-mode: in-out', function (done) {
+      var spy1 = jasmine.createSpy('enter')
+      var spy2 = jasmine.createSpy('leave')
+      var next
+      // === IMPORTANT ===
+      // PhantomJS always returns false when calling
+      // Element.contains() on a comment node. This causes
+      // transitions to be skipped. Monkey patching here
+      // isn't ideal but does the job...
+      var inDoc = _.inDoc
+      _.inDoc = function () {
+        return true
+      }
       var vm = new Vue({
         el: el,
-        data: { ok: true },
-        template: '<div v-component="test" v-if="ok"></div>',
+        data: {
+          view: 'a'
+        },
+        template: '<div v-component="{{view}}" v-transition="test" transition-mode="in-out"></div>',
         components: {
-          test: {}
+          a: { template: 'AAA' },
+          b: { template: 'BBB' }
+        },
+        transitions: {
+          test: {
+            enter: function (el, done) {
+              spy1()
+              next = done
+            },
+            leave: function (el, done) {
+              spy2()
+              done()
+            }
+          }
         }
       })
-      var child = vm._children[0]
-      vm._directives[0].unbind()
-      expect(vm._children.length).toBe(0)
-      expect(child._isDestroyed).toBe(true)
+      expect(el.textContent).toBe('AAA')
+      vm.view = 'b'
+      _.nextTick(function () {
+        expect(spy1).toHaveBeenCalled()
+        expect(spy2).not.toHaveBeenCalled()
+        expect(el.textContent).toBe('AAABBB')
+        next()
+        expect(spy2).toHaveBeenCalled()
+        expect(el.textContent).toBe('BBB')
+        // clean up
+        _.inDoc = inDoc
+        done()
+      })
+    })
+
+    it('transition-mode: out-in', function (done) {
+      var spy1 = jasmine.createSpy('enter')
+      var spy2 = jasmine.createSpy('leave')
+      var next
+      var inDoc = _.inDoc
+      _.inDoc = function () {
+        return true
+      }
+      var vm = new Vue({
+        el: el,
+        data: {
+          view: 'a'
+        },
+        template: '<div v-component="{{view}}" v-transition="test" transition-mode="out-in"></div>',
+        components: {
+          a: { template: 'AAA' },
+          b: { template: 'BBB' }
+        },
+        transitions: {
+          test: {
+            enter: function (el, done) {
+              spy2()
+              done()
+            },
+            leave: function (el, done) {
+              spy1()
+              next = done
+            }
+          }
+        }
+      })
+      expect(el.textContent).toBe('AAA')
+      vm.view = 'b'
+      _.nextTick(function () {
+        expect(spy1).toHaveBeenCalled()
+        expect(spy2).not.toHaveBeenCalled()
+        expect(el.textContent).toBe('AAA')
+        next()
+        expect(spy2).toHaveBeenCalled()
+        expect(el.textContent).toBe('BBB')
+        // clean up
+        _.inDoc = inDoc
+        done()
+      })
+    })
+
+    it('teardown', function (done) {
+      var vm = new Vue({
+        el: el,
+        template: '<div v-component="{{view}}" keep-alive></div>',
+        data: {
+          view: 'test'
+        },
+        components: {
+          test: {},
+          test2: {}
+        }
+      })
+      vm.view = 'test2'
+      _.nextTick(function () {
+        expect(vm._children.length).toBe(2)
+        var child = vm._children[0]
+        var child2 = vm._children[1]
+        vm._directives[0].unbind()
+        expect(vm._directives[0].cache).toBeNull()
+        expect(vm._children.length).toBe(0)
+        expect(child._isDestroyed).toBe(true)
+        expect(child2._isDestroyed).toBe(true)
+        done()
+      })
     })
 
     it('already mounted warn', function () {