diff --git a/src/directives/element/slot.js b/src/directives/element/slot.js index cb9aa6d8394..a2b1312890b 100644 --- a/src/directives/element/slot.js +++ b/src/directives/element/slot.js @@ -2,20 +2,21 @@ import { SLOT } from '../priorities' import { extractContent, replace, - remove + remove, + defineReactive } from '../../util/index' export default { priority: SLOT, - params: ['name'], + params: ['name','plugin'], bind () { // this was resolved during component transclusion var name = this.params.name || 'default' var content = this.vm._slotContents && this.vm._slotContents[name] if (!content || !content.hasChildNodes()) { - this.fallback() + this.compile(extractContent(this.el, true), this.vm) } else { this.compile(content.cloneNode(true), this.vm._context, this.vm) } @@ -38,12 +39,52 @@ export default { elseBlock._context = this.vm content.appendChild(elseBlock) } - const scope = host + let scope = host ? host._scope : this._scope - this.unlink = context.$compile( - content, host, scope, this._frag - ) + + if (this.params.plugin) { + if (!host) { + // TODO warn to use plugin only within components + } + // copy all remaining attributes from slot to all direct children + let attrs = this.el.attributes + for(let i = attrs.length - 1; i >= 0; i--) { + let name = attrs[i].name, + value = attrs[i].value, + children = content.children + for(let j = 0, len = children.length; j < len; j++) { + // TODO warn if child can't have attributes? + if (!(children[j].hasAttribute(name) || + (name[0]===':' && children[j].hasAttribute('v-bind'+name)) || + (name[0]==='@' && children[j].hasAttribute('v-on:'+name.slice(1))) + )) { + children[j].setAttribute(name,value) + } + } + } + // use v-for scope when available + scope = this._frag ? this._frag.scope : scope || {} + + // add all data from context to scope + for(let data in context._data) { + if (scope[data] == null) { + defineReactive(scope,data,context._data[data]) + } + } + // add all data from host to scope with prefixed names + for(let data in host._data) { + defineReactive(scope,'_'+data,host._data[data]) + } + // child relations test: how to get the slot content to be comp11 child? + this.unlink = context.$compile(content, host, scope, this._frag) + if (this.params.plugin !== true) { + // should check on the newly created vm if it is valid + } + + } else { + this.unlink = context.$compile(content, host, scope, this._frag) + } } if (content) { replace(this.el, content) @@ -52,10 +93,6 @@ export default { } }, - fallback () { - this.compile(extractContent(this.el, true), this.vm) - }, - unbind () { if (this.unlink) { this.unlink() diff --git a/test/unit/specs/directives/element/slot_spec.js b/test/unit/specs/directives/element/slot_spec.js index 1007c4d1016..ca4095bd31b 100644 --- a/test/unit/specs/directives/element/slot_spec.js +++ b/test/unit/specs/directives/element/slot_spec.js @@ -457,4 +457,153 @@ describe('Slot Distribution', function () { }) expect('"slot" attribute must be static').toHaveBeenWarned() }) + + it('child relations', function () { + var vm = new Vue({ + el: el, + template: '', + components: { + comp1: { + template: '
', + components: { + comp11: { + template: '
' + } + } + }, + comp2: { + template: '
' + } + } + }) + expect(vm.$children[0].$children.length).toBe(2) + expect(vm.$children[0].$children[0].$children.length).toBe(0) + expect(vm.$children[0].$children[1].$children.length).toBe(0) + }) + + + describe('plugin directive', function() { + + it('scoped data', function () { + var vm = new Vue({ + el: el, + template: '{{text}}', + data: { + text:'main' + }, + components: { + comp1: { + template: '
', + data: function(){ + return { + text:'comp1' + } + } + }, + comp2: { + props: { + text: { + type: String + } + }, + template: '
{{text}}
' + } + } + }) + expect(vm.$el.textContent).toBe('comp1main') + }) + + it('scoped data hierarchy', function () { + var vm = new Vue({ + el: el, + template: '{{text}}', + data: { + text:'main' + }, + components: { + comp1: { + template: '
', + data: function(){ + return { + text:'comp1' + } + } + }, + comp2: { + props: { + text: { + type: String + } + }, + template: '
{{text}}
' + } + } + }) + expect(vm.$el.textContent).toBe('mainmain') + }) + + it('child relations', function () { + var vm = new Vue({ + el: el, + template: '', + components: { + comp1: { + template: '
', + components: { + comp11: { + template: '
' + } + } + }, + comp2: { + template: '
' + } + } + }) + expect(vm.$children[0].$children.length).toBe(1) + expect(vm.$children[0].$children[0].$options.name).toBe("comp11") + expect(vm.$children[0].$children[0].$children[0].$options.name).toBe("comp2") + }) + + it('v-for', function () { + var vm = new Vue({ + el: el, + template: '', + components: { + comp1: { + template: '
', + data: function() { + return {data:[{name:"foo"},{name:"bar"}]} + } + }, + comp2: { + props: { + item: { + type: Object + } + }, + template: '
{{item.name}}
' + } + } + }) + expect(vm.$el.textContent).toBe('foobar') + }) + + it('validate child', function () { + var vm = new Vue({ + el: el, + template: '', + components: { + comp1: { + template: '
' + }, + comp2: { + template: '
foo
' + } + } + }) + expect(vm.$el.textContent).toBe('') + }) + }) + })