Browse Source

v-on: support .stop and .prevent modifiers

Evan You 10 năm trước cách đây
mục cha
commit
d0c916fd9e

+ 47 - 14
src/directives/public/on.js

@@ -1,5 +1,10 @@
 var _ = require('../../util')
 var _ = require('../../util')
 
 
+// modifiers
+var stopRE = /\.stop\b/
+var preventRE = /\.prevent\b/
+
+// keyCode aliases
 var keyCodes = {
 var keyCodes = {
   esc: 27,
   esc: 27,
   tab: 9,
   tab: 9,
@@ -12,15 +17,6 @@ var keyCodes = {
   down: 40
   down: 40
 }
 }
 
 
-/**
- * Wrap a handler function so that it only gets triggered on
- * specified keypress events.
- *
- * @param {Function} handler
- * @param {String|Number} key
- * @return {Function}
- */
-
 function keyFilter (handler, key) {
 function keyFilter (handler, key) {
   var code = keyCodes[key]
   var code = keyCodes[key]
   if (!code) {
   if (!code) {
@@ -33,6 +29,20 @@ function keyFilter (handler, key) {
   }
   }
 }
 }
 
 
+function stopFilter (handler) {
+  return function (e) {
+    e.stopPropagation()
+    return handler.call(this, e)
+  }
+}
+
+function preventFilter (handler) {
+  return function (e) {
+    e.preventDefault()
+    return handler.call(this, e)
+  }
+}
+
 module.exports = {
 module.exports = {
 
 
   acceptStatement: true,
   acceptStatement: true,
@@ -40,11 +50,27 @@ module.exports = {
 
 
   bind: function () {
   bind: function () {
     // 1.0.0 key filter
     // 1.0.0 key filter
-    var rawEvent = this.event = this.arg
-    var keyIndex = rawEvent.indexOf('.')
+    var event = this.arg
+
+    // stop modifier
+    if (stopRE.test(event)) {
+      this.stop = true
+      event = event.replace(stopRE, '')
+    }
+
+    // prevent modifier
+    if (preventRE.test(event)) {
+      this.prevent = true
+      event = event.replace(preventRE, '')
+    }
+
+    // key modifier
+    var keyIndex = event.indexOf('.')
     if (keyIndex > -1) {
     if (keyIndex > -1) {
-      this.event = rawEvent.slice(0, keyIndex)
-      this.key = rawEvent.slice(keyIndex + 1)
+      this.event = event.slice(0, keyIndex)
+      this.key = event.slice(keyIndex + 1)
+    } else {
+      this.event = event
     }
     }
 
 
     // deal with iframes
     // deal with iframes
@@ -63,13 +89,20 @@ module.exports = {
   update: function (handler) {
   update: function (handler) {
     if (typeof handler !== 'function') {
     if (typeof handler !== 'function') {
       process.env.NODE_ENV !== 'production' && _.warn(
       process.env.NODE_ENV !== 'production' && _.warn(
-        'on-"' + this.event + '="' +
+        'v-on:' + this.event + '="' +
         this.expression + '" expects a function value, ' +
         this.expression + '" expects a function value, ' +
         'got ' + handler
         'got ' + handler
       )
       )
       return
       return
     }
     }
 
 
+    // apply modifiers
+    if (this.stop) {
+      handler = stopFilter(handler)
+    }
+    if (this.prevent) {
+      handler = preventFilter(handler)
+    }
     if (this.key) {
     if (this.key) {
       handler = keyFilter(handler, this.key)
       handler = keyFilter(handler, this.key)
     }
     }

+ 55 - 3
test/unit/specs/directives/public/on_spec.js

@@ -68,7 +68,7 @@ if (_.inBrowser) {
       })
       })
     })
     })
 
 
-    it('with key filter', function (done) {
+    it('with key modifier', function (done) {
       new Vue({
       new Vue({
         el: el,
         el: el,
         template: '<a v-on:keyup.enter="test">{{a}}</a>',
         template: '<a v-on:keyup.enter="test">{{a}}</a>',
@@ -89,7 +89,7 @@ if (_.inBrowser) {
       })
       })
     })
     })
 
 
-    it('with key filter (keycode)', function (done) {
+    it('with key modifier (keycode)', function (done) {
       new Vue({
       new Vue({
         el: el,
         el: el,
         template: '<a v-on:keyup.13="test">{{a}}</a>',
         template: '<a v-on:keyup.13="test">{{a}}</a>',
@@ -110,7 +110,59 @@ if (_.inBrowser) {
       })
       })
     })
     })
 
 
-    it('warn nv-on:function values', function () {
+    it('stop modifier', function () {
+      var outer = jasmine.createSpy('outer')
+      var inner = jasmine.createSpy('inner')
+      new Vue({
+        el: el,
+        template: '<div @click="outer"><div class="inner" @click.stop="inner"></div></div>',
+        methods: {
+          outer: outer,
+          inner: inner
+        }
+      })
+      trigger(el.querySelector('.inner'), 'click')
+      expect(inner).toHaveBeenCalled()
+      expect(outer).not.toHaveBeenCalled()
+    })
+
+    it('prevent modifier', function () {
+      var event
+      new Vue({
+        el: el,
+        template: '<a href="#" @click.prevent="onClick">',
+        methods: {
+          onClick: function (e) {
+            event = e
+          }
+        }
+      })
+      trigger(el.firstChild, 'click')
+      expect(event.defaultPrevented).toBe(true)
+    })
+
+    it('multiple modifiers working together', function () {
+      var outer = jasmine.createSpy('outer')
+      var event
+      new Vue({
+        el: el,
+        template: '<div @keyup="outer"><input class="inner" @keyup.enter.stop.prevent="inner"></div></div>',
+        methods: {
+          outer: outer,
+          inner: function (e) {
+            event = e
+          }
+        }
+      })
+      trigger(el.querySelector('.inner'), 'keyup', function (e) {
+        e.keyCode = 13
+      })
+      expect(outer).not.toHaveBeenCalled()
+      expect(event).toBeTruthy()
+      expect(event.defaultPrevented).toBe(true)
+    })
+
+    it('warn non-function values', function () {
       new Vue({
       new Vue({
         el: el,
         el: el,
         data: { test: 123 },
         data: { test: 123 },