Allow template nodes to be passed as a template option

Split the parsing/switching logic of the type of template option
provided into the `template-parser` module and refactored `fragment`
module to only convert string -> document fragment
This commit is contained in:
jordangarcia 2014-07-04 14:04:27 -07:00
parent fe996b3889
commit 66f4708643
7 changed files with 151 additions and 41 deletions

View File

@ -18,6 +18,7 @@
"src/observer.js",
"src/directive.js",
"src/exp-parser.js",
"src/template-parser.js",
"src/text-parser.js",
"src/deps-parser.js",
"src/filters.js",
@ -34,4 +35,4 @@
"src/directives/partial.js",
"src/directives/view.js"
]
}
}

View File

@ -30,29 +30,12 @@ map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'
var TAG_RE = /<([\w:]+)/
module.exports = function (template) {
if (typeof template !== 'string') {
return template
}
// template by ID
if (template.charAt(0) === '#') {
var templateNode = document.getElementById(template.slice(1))
if (!templateNode) return
// if its a template tag and the browser supports it,
// its content is already a document fragment!
if (templateNode.tagName === 'TEMPLATE' && templateNode.content) {
return templateNode.content
}
template = templateNode.innerHTML
}
module.exports = function (templateString) {
var frag = document.createDocumentFragment(),
m = TAG_RE.exec(template)
m = TAG_RE.exec(templateString)
// text only
if (!m) {
frag.appendChild(document.createTextNode(template))
frag.appendChild(document.createTextNode(templateString))
return frag
}
@ -63,7 +46,7 @@ module.exports = function (template) {
suffix = wrap[2],
node = document.createElement('div')
node.innerHTML = prefix + template.trim() + suffix
node.innerHTML = prefix + templateString.trim() + suffix
while (depth--) node = node.lastChild
// one element

View File

@ -28,7 +28,7 @@ assetTypes.forEach(function (type) {
}
if (!value) return hash[id]
if (type === 'partial') {
value = utils.toFragment(value)
value = utils.parseTemplateOption(value)
} else if (type === 'component') {
value = utils.toConstructor(value)
} else if (type === 'filter') {

46
src/template-parser.js Normal file
View File

@ -0,0 +1,46 @@
var toFragment = require('./fragment');
/**
* Parses a template string or node and normalizes it into a
* a node that can be used as a partial of a template option
*
* Possible values include
* id selector: '#some-template-id'
* template string: '<div><span>my template</span></div>'
* DocumentFragment object
* Node object of type Template
*/
module.exports = function(template) {
var templateNode;
if (template instanceof window.DocumentFragment) {
// if the template is already a document fragment -- do nothing
return template
}
if (typeof template === 'string') {
// template by ID
if (template.charAt(0) === '#') {
templateNode = document.getElementById(template.slice(1))
if (!templateNode) return
} else {
return toFragment(template)
}
} else if (template.nodeType) {
templateNode = template
} else {
return
}
// if its a template tag and the browser supports it,
// its content is already a document fragment!
if (templateNode.tagName === 'TEMPLATE' && templateNode.content) {
return templateNode.content
}
if (templateNode.tagName === 'SCRIPT') {
return toFragment(templateNode.innerHTML)
}
return toFragment(templateNode.outerHTML);
}

View File

@ -32,6 +32,11 @@ var utils = module.exports = {
*/
toFragment: require('./fragment'),
/**
* Parse the various types of template options
*/
parseTemplateOption: require('./template-parser.js'),
/**
* get a value from an object keypath
*/
@ -226,7 +231,7 @@ var utils = module.exports = {
}
if (partials) {
for (key in partials) {
partials[key] = utils.toFragment(partials[key])
partials[key] = utils.parseTemplateOption(partials[key])
}
}
if (filters) {
@ -235,7 +240,7 @@ var utils = module.exports = {
}
}
if (template) {
options.template = utils.toFragment(template)
options.template = utils.parseTemplateOption(template)
}
},

View File

@ -1,4 +1,5 @@
describe('Utils', function () {
afterEach(cleanupMocks)
var utils = require('vue/src/utils'),
config = require('vue/src/config')
@ -215,7 +216,7 @@ describe('Utils', function () {
describe('toFragment', function () {
it('should convert a string tempalte to a documentFragment', function () {
it('should convert a string template to a documentFragment', function () {
var template = '<div class="a">hi</div><p>ha</p>',
frag = utils.toFragment(template)
assert.ok(frag instanceof window.DocumentFragment)
@ -223,20 +224,6 @@ describe('Utils', function () {
assert.equal(frag.querySelector('p').textContent, 'ha')
})
it('should also work if the string is an ID selector', function () {
var id = 'utils-template-to-fragment',
template = '<div class="a">hi</div><p>ha</p>',
el = document.createElement('template')
el.id = id
el.innerHTML = template
document.getElementById('test').appendChild(el)
var frag = utils.toFragment('#' + id)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
it('should work with table elements', function () {
var frag = utils.toFragment('<td></td>')
assert.ok(frag instanceof window.DocumentFragment)
@ -252,6 +239,75 @@ describe('Utils', function () {
})
describe('parseTemplateOption', function () {
afterEach(cleanupMocks)
it('should convert a string template to a documentFragment', function () {
var template = '<div class="a">hi</div><p>ha</p>',
frag = utils.parseTemplateOption(template)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
describe('id selector', function() {
it('should work with a TEMPLATE tag', function() {
var id = 'utils-template-parse-template-option-template-tag',
template = '<div class="a">hi</div><p>ha</p>',
el = document.createElement('template')
el.id = id
el.innerHTML = template
appendMock(el)
var frag = utils.parseTemplateOption('#' + id)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
it('should work with a DIV tag', function() {
var id = 'utils-template-parse-template-option-div-tag',
template = '<div class="a">hi</div><p>ha</p>',
el = document.createElement('div')
el.id = id
el.innerHTML = template
appendMock(el)
var frag = utils.parseTemplateOption('#' + id)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
})
describe('when passed a Node', function() {
it('should work with a TEMPLATE tag', function() {
var el = document.createElement('template')
el.innerHTML = '<div class="a">hi</div><p>ha</p>'
var frag = utils.parseTemplateOption(el)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
it('should work with a DIV tag', function() {
var el = document.createElement('div')
el.innerHTML = '<span class="a">hi</span><p>ha</p>'
var frag = utils.parseTemplateOption(el)
assert.ok(frag instanceof window.DocumentFragment)
assert.equal(frag.firstChild.outerHTML, el.outerHTML)
assert.equal(frag.querySelector('.a').textContent, 'hi')
assert.equal(frag.querySelector('p').textContent, 'ha')
})
})
})
describe('toConstructor', function () {
it('should convert an non-VM object to a VM constructor', function () {
@ -270,6 +326,17 @@ describe('Utils', function () {
describe('processOptions', function () {
beforeEach(function() {
var id = 'utils-template-to-fragment',
template = '<div class="a">hi</div><p>ha</p>',
el = document.createElement('template')
el.id = id
el.innerHTML = template
appendMock(el)
})
afterEach(cleanupMocks)
var options = {
partials: {
a: '#utils-template-to-fragment',

View File

@ -7,10 +7,18 @@ function mock (id, html, attrs) {
el.setAttribute(attr, attrs[attr])
}
}
document.getElementById('test').appendChild(el)
appendMock(el)
return el
}
function appendMock(el) {
document.getElementById('test').appendChild(el)
}
function cleanupMocks() {
document.getElementById('test').innerHTML = ''
}
function mockHTMLEvent (type) {
var e = document.createEvent('HTMLEvents')
e.initEvent(type, true, true)