Bläddra i källkod

{{{ }}} tags for unescaped html

Evan You 12 år sedan
förälder
incheckning
58dd07e575

+ 2 - 1
component.json

@@ -27,7 +27,8 @@
         "src/directives/repeat.js",
         "src/directives/on.js",
         "src/directives/model.js",
-        "src/directives/with.js"
+        "src/directives/with.js",
+        "src/directives/html.js"
     ],
     "dependencies": {
         "component/emitter": "*"

+ 7 - 5
src/compiler.js

@@ -357,6 +357,7 @@ CompilerProto.compileTextNode = function (node) {
 
     for (var i = 0, l = tokens.length; i < l; i++) {
         token = tokens[i]
+        directive = null
         if (token.key) { // a binding
             if (token.key.charAt(0) === '>') { // a partial
                 partialId = token.key.slice(1).trim()
@@ -368,14 +369,12 @@ CompilerProto.compileTextNode = function (node) {
                     partialNodes = slice.call(el.childNodes)
                 }
             } else { // a real binding
-                if (!token.html) // text binding
+                if (!token.html) { // text binding
                     el = document.createTextNode('')
                     directive = Directive.parse('text', token.key, this, el)
-                    if (directive) {
-                        this.bindDirective(directive)
-                    }
                 } else { // html binding
-                    
+                    el = document.createComment(config.prefix + '-html')
+                    directive = Directive.parse('html', token.key, this, el)
                 }
             }
         } else { // a plain string
@@ -384,6 +383,9 @@ CompilerProto.compileTextNode = function (node) {
 
         // insert node
         node.parentNode.insertBefore(el, node)
+        if (directive) {
+            this.bindDirective(directive)
+        }
 
         // compile partial after appending, because its children's parentNode
         // will change from the fragment to the correct parentNode.

+ 39 - 0
src/directives/html.js

@@ -0,0 +1,39 @@
+var toText = require('../utils').toText,
+    slice = Array.prototype.slice
+
+module.exports = {
+
+    bind: function () {
+        // a comment node means this is a binding for
+        // {{{ inline unescaped html }}}
+        if (this.el.nodeType === 8) {
+            // hold nodes
+            this.holder = document.createElement('div')
+            this.nodes = []
+        }
+    },
+
+    update: function (value) {
+        value = toText(value)
+        if (this.holder) {
+            this.swap(value)
+        } else {
+            this.el.innerHTML = value
+        }
+    },
+
+    swap: function (value) {
+        var parent = this.el.parentNode,
+            holder = this.holder,
+            nodes = this.nodes,
+            i = nodes.length, l
+        while (i--) {
+            parent.removeChild(nodes[i])
+        }
+        holder.innerHTML = value
+        nodes = this.nodes = slice.call(holder.childNodes)
+        for (i = 0, l = nodes.length; i < l; i++) {
+            parent.insertBefore(nodes[i], this.el)
+        }
+    }
+}

+ 1 - 4
src/directives/index.js

@@ -8,6 +8,7 @@ module.exports = {
     model     : require('./model'),
     'if'      : require('./if'),
     'with'    : require('./with'),
+    html      : require('./html'),
 
     attr: function (value) {
         this.el.setAttribute(this.arg, value)
@@ -17,10 +18,6 @@ module.exports = {
         this.el.textContent = utils.toText(value)
     },
 
-    html: function (value) {
-        this.el.innerHTML = utils.toText(value)
-    },
-
     show: function (value) {
         var el = this.el,
             target = value ? '' : 'none',

+ 8 - 0
test/functional/fixtures/expression.html

@@ -24,6 +24,7 @@
             <button v-on="click: noMsg = 'Nah'" class="change">change</button>
         </div>
         <div id="attrs" data-test="hi {{msg}} ha"></div>
+        <div id="html">html {{{html}}} work</div>
         <script>
             Vue.config({debug:true})
 
@@ -63,6 +64,13 @@
                     msg: 'ho'
                 }
             })
+
+            var html = new Vue({
+                el: '#html',
+                data: {
+                    html: '<p>should</p> <a>probably</a>'
+                }
+            })
         </script>
     </body>
 </html>

+ 18 - 5
test/functional/specs/expression.js

@@ -1,6 +1,6 @@
-/* global normal, attrs */
+/* global normal, attrs, html */
 
-casper.test.begin('Expression', 21, function (test) {
+casper.test.begin('Expression', 23, function (test) {
     
     casper
     .start('./fixtures/expression.html')
@@ -75,9 +75,22 @@ casper.test.begin('Expression', 21, function (test) {
         attrs.msg = 'hoho'
     })
     .then(function () {
-        test.assertEval(function () {
-            return document.getElementById('attrs').dataset.test === 'hi hoho ha'
-        })
+        // attr
+        test.assertEvalEquals(function () {
+            return document.getElementById('attrs').dataset.test
+        }, 'hi hoho ha')
+        // html
+        test.assertEvalEquals(function () {
+            return document.getElementById('html').innerHTML
+        }, 'html <p>should</p> <a>probably</a><!--v-html--> work')
+    })
+    .thenEvaluate(function () {
+        html.html = '<span>should</span> <a>change work</a>'
+    })
+    .then(function () {
+        test.assertEvalEquals(function () {
+            return document.getElementById('html').innerHTML
+        }, 'html <span>should</span> <a>change work</a><!--v-html--> work')
     })
     .run(function () {
         test.done()

+ 22 - 0
test/unit/specs/directives.js

@@ -76,6 +76,28 @@ describe('UNIT: Directives', function () {
             assert.strictEqual(dir.el.innerHTML, '')
         })
 
+        it('should swap html if el is a comment placeholder', function () {
+            var dir = mockDirective('html'),
+                comment = document.createComment('hi'),
+                parent = dir.el
+            parent.innerHTML = 'what!'
+            parent.appendChild(comment)
+            dir.el = comment
+
+            dir.bind()
+            assert.ok(dir.holder)
+            assert.ok(dir.nodes)
+
+            var pre = 'what!',
+                after = '<!--hi-->',
+                h1 = '<span>hello</span><span>world</span>',
+                h2 = '<a>whats</a><a>up</a>'
+            dir.update(h1)
+            assert.strictEqual(parent.innerHTML, pre + h1 + after)
+            dir.update(h2)
+            assert.strictEqual(parent.innerHTML, pre + h2 + after)
+        })
+
     })
 
     describe('show', function () {