Evan You 12 лет назад
Родитель
Сommit
4e062e9270

+ 4 - 0
changes.md

@@ -71,6 +71,10 @@ computed: {
 
 
 - `isFn` is no longer necessary for directives expecting function values.
 - `isFn` is no longer necessary for directives expecting function values.
 
 
+## Interpolation
+
+Text bindings will no longer automatically stringify objects. Use the new `json` filter which gives more flexibility in formatting. Also, `null` will now be printed as is; only `undefined` will yield empty string.
+
 ## Two Way filters
 ## Two Way filters
 
 
 If a filter is defined as a function, it is treated as a read filter by default - i.e. it is applied when data is read from the model and applied to the DOM. You can now specify write filters as well, which are applied when writing to the model, triggered by user input. Write filters are only triggered on two-way bindings like `v-model`.
 If a filter is defined as a function, it is treated as a read filter by default - i.e. it is applied when data is read from the model and applied to the DOM. You can now specify write filters as well, which are applied when writing to the model, triggered by user input. Write filters are only triggered on two-way bindings like `v-model`.

+ 20 - 0
src/directives/attr.js

@@ -0,0 +1,20 @@
+var _ = require('../util')
+
+exports.bind = function () {
+  var params = this.vm.$options.paramAttributes
+  this.isParam =
+    this.el.__vue__ && // only check rootNode for params
+    params &&
+    params.indexOf(this.arg) > -1
+}
+
+exports.update = function (value) {
+  if (value || value === 0) {
+    this.el.setAttribute(this.arg, value)
+  } else {
+    this.el.removeAttribute(this.arg)
+  }
+  if (this.isParam) {
+    this.vm[this.arg] = _.guardNumber(value)
+  }
+}

+ 33 - 0
src/directives/html.js

@@ -0,0 +1,33 @@
+var _ = require('../util')
+var templateParser = require('../parse/template')
+
+exports.bind = function () {
+  // a comment node means this is a binding for
+  // {{{ inline unescaped html }}}
+  if (this.el.nodeType === 8) {
+    // hold nodes
+    this.nodes = []
+  }
+}
+
+exports.update = function (value) {
+  value = _.guard(value)
+  if (this.nodes) {
+    this.swap(value)
+  } else {
+    this.el.innerHTML = value
+  }
+}
+
+exports.swap = function (value) {
+  // remove old nodes
+  var i = this.nodes.length
+  while (i--) {
+    _.remove(this.nodes[i])
+  }
+  // convert new value to a fragment
+  var frag = templateParser.parse(value, true)
+  // save a reference to these nodes so we can remove later
+  this.nodes = _.toArray(frag.childNodes)
+  _.before(frag, this.el)
+}

+ 18 - 1
src/directives/index.js

@@ -1 +1,18 @@
-module.exports = Object.create(null)
+var directives = module.exports = Object.create(null)
+
+directives.text     = require('./text')
+directives.html     = require('./html')
+directives.attr     = require('./attr')
+// directives.show     = require('./show')
+// directives['class'] = require('./class')
+// directives.ref      = require('./ref')
+// directives.cloak    = require('./cloak')
+// directives.on       = require('./on')
+// directives.repeat   = require('./repeat')
+// directives.model    = require('./model')
+// directives['if']    = require('./if')
+// directives['with']  = require('./with')
+// directives.html     = require('./html')
+// directives.style    = require('./style')
+// directives.partial  = require('./partial')
+// directives.view     = require('./view')

+ 11 - 0
src/directives/text.js

@@ -0,0 +1,11 @@
+var _ = require('../util')
+
+exports.bind = function () {
+  this.attr = this.el.nodeType === 3
+    ? 'nodeValue'
+    : 'textContent'
+}
+
+exports.update = function (value) {
+  this.el[this.attr] = _.guard(value)
+}

+ 66 - 1
src/instance/compile.js

@@ -1,7 +1,9 @@
 var _ = require('../util')
 var _ = require('../util')
 var config = require('../config')
 var config = require('../config')
 var Direcitve = require('../directive')
 var Direcitve = require('../directive')
+var textParser = require('../parse/text')
 var dirParser = require('../parse/directive')
 var dirParser = require('../parse/directive')
+var templateParser = require('../parse/template')
 
 
 /**
 /**
  * The main entrance to the compilation process.
  * The main entrance to the compilation process.
@@ -109,6 +111,8 @@ exports._compileAttrs = function (node) {
       } else {
       } else {
         _.warn('Unknown directive: ' + dirName)
         _.warn('Unknown directive: ' + dirName)
       }
       }
+    } else if (config.interpolate) {
+      this._bindAttr(node, attr)
     }
     }
   }
   }
   // sort the directives by priority, low to high
   // sort the directives by priority, low to high
@@ -131,7 +135,38 @@ exports._compileAttrs = function (node) {
  */
  */
 
 
 exports._compileTextNode = function (node) {
 exports._compileTextNode = function (node) {
-  
+  var tokens = textParser.parse(node.nodeValue)
+  if (!tokens) {
+    return
+  }
+  var el, token, value
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i]
+    if (token.tag) {
+      if (token.oneTime) {
+        value = this.$get(token.value)
+        el = token.html
+          ? templateParser.parse(value, true)
+          : document.createTextNode(value)
+        _.before(el, node)
+      } else {
+        value = token.value
+        if (token.html) {
+          el = document.createComment('vue-html')
+          _.before(el, node)
+          this._bindDirective('html', value, el)
+        } else {
+          el = document.createTextNode('')
+          _.before(el, node)
+          this._bindDirective('text', value, el)
+        }
+      }
+    } else {
+      el = document.createTextNode(token.value)
+      _.before(el, node)
+    }
+  }
+  _.remove(node)
 }
 }
 
 
 /**
 /**
@@ -144,6 +179,36 @@ exports._compileComment = function (node) {
   
   
 }
 }
 
 
+/**
+ * Check an attribute for potential bindings
+ */
+
+exports._bindAttr = function (node, attr) {
+  var tokens = textParser.parse(attr.value)
+  if (!tokens) {
+    return
+  }
+  var expression = tokens.map(expifyToken).join('+')
+  this._bindDirective(
+    'attr',
+    attr.name + ':' + expression,
+    node
+  )
+}
+
+/**
+ * Helper to translate token value into expression parts.
+ *
+ * @param {Object} token
+ * @return {String}
+ */
+
+function expifyToken (token) {
+  return token.tag
+    ? token.value
+    : ("'" + token.value + "'")
+}
+
 /**
 /**
  * Check for priority directives that would potentially
  * Check for priority directives that would potentially
  * skip other directives:
  * skip other directives:

+ 1 - 2
src/instance/element.js

@@ -43,13 +43,12 @@ exports._initTemplate = function () {
   var options = this.$options
   var options = this.$options
   var template = options.template
   var template = options.template
   if (template) {
   if (template) {
-    var frag = templateParser.parse(template)
+    var frag = templateParser.parse(template, true)
     if (!frag) {
     if (!frag) {
       _.warn('Invalid template option: ' + template)
       _.warn('Invalid template option: ' + template)
     } else {
     } else {
       // collect raw content. this wipes out $el.
       // collect raw content. this wipes out $el.
       this._collectRawContent()
       this._collectRawContent()
-      frag = frag.cloneNode(true)
       if (options.replace) {
       if (options.replace) {
         // replace
         // replace
         if (frag.childNodes.length > 1) {
         if (frag.childNodes.length > 1) {

+ 5 - 2
src/parse/template.js

@@ -115,10 +115,11 @@ function nodeToFragment (node) {
  *    - Node object of type Template
  *    - Node object of type Template
  *    - id selector: '#some-template-id'
  *    - id selector: '#some-template-id'
  *    - template string: '<div><span>{{msg}}</span></div>'
  *    - template string: '<div><span>{{msg}}</span></div>'
+ * @param {Boolean} clone
  * @return {DocumentFragment|undefined}
  * @return {DocumentFragment|undefined}
  */
  */
 
 
-exports.parse = function (template) {
+exports.parse = function (template, clone) {
   var node, frag
   var node, frag
 
 
   // if the template is already a document fragment,
   // if the template is already a document fragment,
@@ -149,5 +150,7 @@ exports.parse = function (template) {
     frag = nodeToFragment(template)
     frag = nodeToFragment(template)
   }
   }
 
 
-  return frag
+  return frag && clone
+    ? frag.cloneNode(true)
+    : frag
 }
 }

+ 3 - 1
src/util/dom.js

@@ -10,7 +10,9 @@ var config = require('../config')
 exports.attr = function (node, attr) {
 exports.attr = function (node, attr) {
   attr = config.prefix + attr
   attr = config.prefix + attr
   var val = node.getAttribute(attr)
   var val = node.getAttribute(attr)
-  node.removeAttribute(attr)
+  if (val !== null) {
+    node.removeAttribute(attr)
+  }
   return val
   return val
 }
 }
 
 

+ 31 - 0
src/util/lang.js

@@ -1,3 +1,34 @@
+/**
+ * Guard text output, make sure undefined outputs
+ * empty string
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+exports.guard = function (value) {
+  return value === undefined
+    ? ''
+    : value
+}
+
+/**
+ * Check and convert possible numeric numbers before
+ * setting back to data
+ *
+ * @param {*} value
+ * @return {*|Number}
+ */
+
+exports.guardNumber = function (value) {
+  return (
+    isNaN(value) ||
+    value === null ||
+    typeof value === 'boolean'
+  ) ? value
+    : Number(value)
+}
+
 /**
 /**
  * Simple bind, faster than native
  * Simple bind, faster than native
  *
  *

+ 6 - 0
test/unit/specs/parse_template_spec.js

@@ -99,6 +99,12 @@ if (_.inBrowser) {
       expect(res1).toBe(res2)
       expect(res1).toBe(res2)
     })
     })
 
 
+    it('should clone', function () {
+      var res1 = parse(testString, true)
+      var res2 = parse(testString, true)
+      expect(res1).not.toBe(res2)
+    })
+
     it('should cache id selectors', function () {
     it('should cache id selectors', function () {
       var node = document.createElement('script')
       var node = document.createElement('script')
       node.setAttribute('id', 'template-test')
       node.setAttribute('id', 'template-test')

+ 7 - 0
test/unit/specs/util_dom_spec.js

@@ -16,6 +16,13 @@ if (_.inBrowser) {
       target = div()
       target = div()
       parent.appendChild(child) 
       parent.appendChild(child) 
     })
     })
+
+    it('attr', function () {
+      target.setAttribute('v-test', 'ok')
+      var val = _.attr(target, 'test')
+      expect(val).toBe('ok')
+      expect(target.hasAttribute('v-test')).toBe(false)
+    })
     
     
     it('before', function () {
     it('before', function () {
       _.before(target, child)
       _.before(target, child)

+ 15 - 0
test/unit/specs/util_lang_spec.js

@@ -2,6 +2,21 @@ var _ = require('../../../src/util')
 
 
 describe('Util - Language Enhancement', function () {
 describe('Util - Language Enhancement', function () {
 
 
+  it('guard', function () {
+    expect(_.guard(1)).toBe(1)
+    expect(_.guard(null)).toBe(null)
+    expect(_.guard(undefined)).toBe('')
+  })
+
+  it('guardNumber', function () {
+    expect(_.guardNumber('12')).toBe(12)
+    expect(_.guardNumber('1e5')).toBe(1e5)
+    expect(_.guardNumber('0x2F')).toBe(0x2F)
+    expect(_.guardNumber(null)).toBe(null)
+    expect(_.guardNumber(true)).toBe(true)
+    expect(_.guardNumber('hello')).toBe('hello')
+  })
+
   it('bind', function () {
   it('bind', function () {
     var original = function (a) {
     var original = function (a) {
       return this.a + a
       return this.a + a