From 3cbf498d5a3d709fcf96ade11c65d25e05afbba4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 8 Oct 2013 17:22:48 -0400 Subject: [PATCH] partials done. --- src/compiler.js | 50 ++++++++++++++++---------- src/directive.js | 9 +++-- src/directives/each.js | 7 +--- src/main.js | 13 +++++++ test/unit/specs/api.js | 64 +++++++++++++++++++++++++++++----- test/unit/specs/directive.js | 1 + test/unit/specs/text-parser.js | 9 +++-- 7 files changed, 112 insertions(+), 41 deletions(-) diff --git a/src/compiler.js b/src/compiler.js index 00c5b062963..122980c879f 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -173,10 +173,9 @@ CompilerProto.compile = function (node, root) { var compiler = this if (node.nodeType === 1) { // a normal node - var opts = compiler.options, - eachExp = node.getAttribute(eachAttr), - vmExp = node.getAttribute(vmAttr), - partialExp = node.getAttribute(partialAttr) + var eachExp = node.getAttribute(eachAttr), + vmId = node.getAttribute(vmAttr), + partialId = node.getAttribute(partialAttr) // we need to check for any possbile special directives // e.g. sd-each, sd-viewmodel & sd-partial if (eachExp) { // each block @@ -184,11 +183,9 @@ CompilerProto.compile = function (node, root) { if (directive) { compiler.bindDirective(directive) } - } else if (vmExp && !root) { // nested ViewModels + } else if (vmId && !root) { // nested ViewModels node.removeAttribute(vmAttr) - var ChildVM = - (opts.vms && opts.vms[vmExp]) || - utils.vms[vmExp] + var ChildVM = compiler.getOption('vms', vmId) if (ChildVM) { new ChildVM({ el: node, @@ -199,11 +196,9 @@ CompilerProto.compile = function (node, root) { }) } } else { - if (partialExp) { // replace innerHTML with partial + if (partialId) { // replace innerHTML with partial node.removeAttribute(partialAttr) - var partial = - (opts.partials && opts.partials[partialExp]) || - utils.partials[partialExp] + var partial = compiler.getOption('partials', partialId) if (partial) { node.innerHTML = '' node.appendChild(partial.cloneNode(true)) @@ -265,14 +260,23 @@ CompilerProto.compileTextNode = function (node) { el, token, directive for (var i = 0, l = tokens.length; i < l; i++) { token = tokens[i] - el = document.createTextNode('') - if (token.key) { - directive = Directive.parse(dirname, token.key, this, el) - if (directive) { - this.bindDirective(directive) + if (token.key) { // a binding + if (token.key.charAt(0) === '>') { // a partial + var partialId = token.key.slice(1), + partial = this.getOption('partials', partialId) + if (partial) { + el = partial.cloneNode(true) + this.compileNode(el) + } + } else { // a binding + el = document.createTextNode('') + directive = Directive.parse(dirname, token.key, this, el) + if (directive) { + this.bindDirective(directive) + } } - } else { - el.nodeValue = token + } else { // a plain string + el = document.createTextNode(token) } node.parentNode.insertBefore(el, node) } @@ -505,6 +509,14 @@ CompilerProto.bindContexts = function (bindings) { } } +/* + * Retrive an option from the compiler + */ +CompilerProto.getOption = function (type, id) { + var opts = this.options + return (opts[type] && opts[type][id]) || (utils[type] && utils[type][id]) +} + /* * Unbind and remove element */ diff --git a/src/directive.js b/src/directive.js index 3261ad18bde..d732773ad7e 100644 --- a/src/directive.js +++ b/src/directive.js @@ -46,7 +46,7 @@ function Directive (definition, directiveName, expression, rawKey, compiler, nod this.filters = [] var i = 0, l = filterExps.length, filter for (; i < l; i++) { - filter = parseFilter(filterExps[i], this.compiler.options) + filter = parseFilter(filterExps[i], this.compiler) if (filter) this.filters.push(filter) } if (!this.filters.length) this.filters = null @@ -91,7 +91,7 @@ function parseKey (dir, rawKey) { /* * parse a filter expression */ -function parseFilter (filter, options) { +function parseFilter (filter, compiler) { var tokens = filter.slice(1).match(FILTER_TOKEN_RE) if (!tokens) return @@ -100,7 +100,7 @@ function parseFilter (filter, options) { }) var name = tokens[0], - apply = (options.filters && options.filters[name]) || filters[name] + apply = compiler.getOption('filters', name) || filters[name] if (!apply) { utils.warn('Unknown filter: ' + name) return @@ -190,8 +190,7 @@ Directive.parse = function (dirname, expression, compiler, node) { if (dirname.indexOf(prefix) === -1) return null dirname = dirname.slice(prefix.length + 1) - var opts = compiler.options, - dir = (opts.directives && opts.directives[dirname]) || directives[dirname], + var dir = compiler.getOption('directives', dirname) || directives[dirname], keyMatch = expression.match(KEY_RE), rawKey = keyMatch && keyMatch[0].trim() diff --git a/src/directives/each.js b/src/directives/each.js index 8081c759289..fc0e1e8b8b4 100644 --- a/src/directives/each.js +++ b/src/directives/each.js @@ -1,5 +1,4 @@ var config = require('../config'), - utils = require('../utils'), Observer = require('../observer'), Emitter = require('../emitter'), ViewModel // lazy def to avoid circular dependency @@ -139,11 +138,7 @@ module.exports = { var node = this.el.cloneNode(true), ctn = this.container, vmID = node.getAttribute(config.prefix + '-viewmodel'), - opts = this.compiler.options, - ChildVM = - (opts.vms && opts.vms[vmID]) || - utils.vms[vmID] || - ViewModel, + ChildVM = this.compiler.getOption('vms', vmID) || ViewModel, wrappedData = {} wrappedData[this.arg] = data || {} var item = new ChildVM({ diff --git a/src/main.js b/src/main.js index 1faaeefff78..a54f39ab9cf 100644 --- a/src/main.js +++ b/src/main.js @@ -109,6 +109,7 @@ function extend (options) { */ function inheritOptions (child, parent, topLevel) { child = child || {} + convertPartials(child.partials) if (!parent) return child for (var key in parent) { if (key === 'el' || key === 'props') continue @@ -121,6 +122,18 @@ function inheritOptions (child, parent, topLevel) { return child } +/* + * Convert an object of partials to dom fragments + */ +function convertPartials (partials) { + if (!partials) return + for (var key in partials) { + if (typeof partials[key] === 'string') { + partials[key] = templateToFragment(partials[key]) + } + } +} + /* * Convert a string template to a dom fragment */ diff --git a/test/unit/specs/api.js b/test/unit/specs/api.js index 2a870108175..767a4e05e76 100644 --- a/test/unit/specs/api.js +++ b/test/unit/specs/api.js @@ -167,16 +167,28 @@ describe('UNIT: API', function () { assert.strictEqual(utils.partials[testId], seed.partial(testId)) }) - it('should work with sd-partial', function () { - mock(testId, 'hello', { - 'sd-partial': testId + it('should work with sd-partial as a directive', function () { + var testId = 'api-partial-direcitve' + seed.partial(testId, partial) + mock(testId, '
hello
') + var t = new seed.ViewModel({ + el: '#' + testId, + data: { hi: 'hohoho' } }) + assert.strictEqual(t.$el.querySelector('.directive .partial-test a').textContent, 'hohoho') + assert.strictEqual(t.$el.querySelector('.directive span').innerHTML, 'hahaha') + }) + + it('should work with sd-partial as an inline interpolation', function () { + var testId = 'api-partial-inline' + seed.partial(testId, partial) + mock(testId, '
{{>' + testId + '}}
') var t = new seed.ViewModel({ el: '#' + testId, data: { hi: 'hohoho' } }) - assert.strictEqual(t.$el.querySelector('.partial-test a').textContent, 'hohoho') - assert.strictEqual(t.$el.querySelector('span').innerHTML, 'hahaha') + assert.strictEqual(t.$el.querySelector('.inline .partial-test a').textContent, 'hohoho') + assert.strictEqual(t.$el.querySelector('.inline span').innerHTML, 'hahaha') }) }) @@ -441,15 +453,49 @@ describe('UNIT: API', function () { }) describe('vms', function () { - it('should be tested', function () { - assert.ok(false) + + it('should allow the VM to use private child VMs', function () { + var Child = seed.ViewModel.extend({ + data: { + name: 'child' + } + }) + var Parent = seed.ViewModel.extend({ + template: '

{{name}}

{{name}}
', + data: { + name: 'dad' + }, + vms: { + child: Child + } + }) + var p = new Parent() + assert.strictEqual(p.$el.querySelector('p').textContent, 'dad') + assert.strictEqual(p.$el.querySelector('div').textContent, 'child') }) + }) describe('partials', function () { - it('should be tested', function () { - assert.ok(false) + + it('should allow the VM to use private partials', function () { + var Test = seed.ViewModel.extend({ + attributes: { + 'sd-partial': 'test' + }, + partials: { + test: '{{a}}

{{b}}

' + }, + data: { + a: 'hi', + b: 'ho' + } + }) + var t = new Test() + assert.strictEqual(t.$el.querySelector('a').textContent, 'hi') + assert.strictEqual(t.$el.querySelector('p').textContent, 'ho') }) + }) describe('transitions', function () { diff --git a/test/unit/specs/directive.js b/test/unit/specs/directive.js index 24621868a1f..18e103fd069 100644 --- a/test/unit/specs/directive.js +++ b/test/unit/specs/directive.js @@ -5,6 +5,7 @@ describe('UNIT: Directive', function () { var compiler = { options: {}, + getOption: function () {}, vm: { constructor: {} } diff --git a/test/unit/specs/text-parser.js b/test/unit/specs/text-parser.js index 1e08773ff9e..0bf2ed131f8 100644 --- a/test/unit/specs/text-parser.js +++ b/test/unit/specs/text-parser.js @@ -16,16 +16,17 @@ describe('UNIT: TextNode Parser', function () { assert.strictEqual(result[2], ' {{hello}}') }) - var tokens = TextParser.parse('hello {{a}}! {{ bcd }}{{d.e.f}} {{a + (b || c) ? d : e}}') + var tokens = TextParser.parse('hello {{a}}! {{ bcd }}{{d.e.f}} {{a + (b || c) ? d : e}} {{>test}}') it('should extract correct amount of tokens', function () { - assert.strictEqual(tokens.length, 7) + assert.strictEqual(tokens.length, 9) }) it('should extract plain strings', function () { assert.strictEqual(typeof tokens[0], 'string') assert.strictEqual(typeof tokens[2], 'string') assert.strictEqual(typeof tokens[5], 'string') + assert.strictEqual(typeof tokens[7], 'string') }) it('should extract basic keys', function () { @@ -44,6 +45,10 @@ describe('UNIT: TextNode Parser', function () { assert.strictEqual(tokens[6].key, 'a + (b || c) ? d : e') }) + it('should extract partials', function () { + assert.strictEqual(tokens[8].key, '>test') + }) + }) describe('.buildRegex()', function () {