mirror of https://github.com/ElemeFE/element
Add v-sync directive. (#598)
parent
4e36ff0eb5
commit
112eb485a0
|
@ -1,3 +1,4 @@
|
||||||
|
import Sync from './utils/sync';
|
||||||
import Pagination from '../packages/pagination/index.js';
|
import Pagination from '../packages/pagination/index.js';
|
||||||
import Dialog from '../packages/dialog/index.js';
|
import Dialog from '../packages/dialog/index.js';
|
||||||
import Autocomplete from '../packages/autocomplete/index.js';
|
import Autocomplete from '../packages/autocomplete/index.js';
|
||||||
|
@ -58,6 +59,7 @@ const install = function(Vue) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (install.installed) return;
|
if (install.installed) return;
|
||||||
|
|
||||||
|
Vue.directive('sync', Sync);
|
||||||
Vue.component(Pagination.name, Pagination);
|
Vue.component(Pagination.name, Pagination);
|
||||||
Vue.component(Dialog.name, Dialog);
|
Vue.component(Dialog.name, Dialog);
|
||||||
Vue.component(Autocomplete.name, Autocomplete);
|
Vue.component(Autocomplete.name, Autocomplete);
|
||||||
|
@ -128,6 +130,7 @@ if (typeof window !== 'undefined' && window.Vue) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
version: '1.0.0-rc.7',
|
version: '1.0.0-rc.7',
|
||||||
install,
|
install,
|
||||||
|
Sync,
|
||||||
Pagination,
|
Pagination,
|
||||||
Dialog,
|
Dialog,
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
const SYNC_HOOK_PROP = '$v-sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v-sync directive
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* v-sync:component-prop="context prop name"
|
||||||
|
*
|
||||||
|
* If your want to sync component's prop "visible" to context prop "myVisible", use like this:
|
||||||
|
* v-sync:visible="myVisible"
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
bind(el, binding, vnode) {
|
||||||
|
const context = vnode.context;
|
||||||
|
const component = vnode.child;
|
||||||
|
const expression = binding.expression;
|
||||||
|
const prop = binding.arg;
|
||||||
|
|
||||||
|
if (!expression || !prop) {
|
||||||
|
console.warn('v-sync should specify arg & expression, for example: v-sync:visible="myVisible"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component || !component.$watch) {
|
||||||
|
console.warn('v-sync is only available on Vue Component');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unwatchContext = context.$watch(expression, (val) => {
|
||||||
|
component[prop] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
const unwatchComponent = component.$watch(prop, (val) => {
|
||||||
|
context[expression] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(component, SYNC_HOOK_PROP, {
|
||||||
|
value: {
|
||||||
|
unwatchContext,
|
||||||
|
unwatchComponent
|
||||||
|
},
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
unbind(el, binding, vnode) {
|
||||||
|
const component = vnode.child;
|
||||||
|
if (component && component[SYNC_HOOK_PROP]) {
|
||||||
|
const { unwatchContext, unwatchComponent } = component[SYNC_HOOK_PROP];
|
||||||
|
unwatchContext && unwatchContext();
|
||||||
|
unwatchComponent && unwatchComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,151 @@
|
||||||
|
import { createVue, triggerEvent } from '../util';
|
||||||
|
import Sync from 'element-ui/src/utils/sync';
|
||||||
|
|
||||||
|
const Test = {
|
||||||
|
template: `<div class="sync-test" v-show="visible">
|
||||||
|
<button @click="visible = false">Hide</button>
|
||||||
|
A test component.
|
||||||
|
</div>`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Sync', () => {
|
||||||
|
it('should not throw when use incorrectly', () => {
|
||||||
|
createVue({
|
||||||
|
template: `
|
||||||
|
<test v-sync>
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createVue({
|
||||||
|
template: `
|
||||||
|
<test v-sync:visible>
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createVue({
|
||||||
|
template: `
|
||||||
|
<test v-sync.visible>
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
createVue({
|
||||||
|
template: `
|
||||||
|
<div v-sync:visible="myVisible">
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('context variable should change when inner component variable change', (done) => {
|
||||||
|
const vm = createVue({
|
||||||
|
template: `
|
||||||
|
<test v-sync:visible="myVisible">
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
triggerEvent(vm.$el.querySelector('.sync-test button'), 'click', {});
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.myVisible).to.be.false;
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inner component variable should change when context variable change', (done) => {
|
||||||
|
const vm = createVue({
|
||||||
|
template: `
|
||||||
|
<test ref="test" v-sync:visible="myVisible">
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
vm.myVisible = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.$refs.test.visible).to.be.false;
|
||||||
|
expect(vm.$refs.test.$el.style.display).to.equal('none');
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unwatch expression after destroy', () => {
|
||||||
|
const vm = createVue({
|
||||||
|
template: `
|
||||||
|
<test ref="test" v-sync:visible="myVisible" v-if="createTest">
|
||||||
|
</test>
|
||||||
|
`,
|
||||||
|
components: { Test },
|
||||||
|
directives: { Sync },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
myVisible: true,
|
||||||
|
createTest: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const beforeBindCount = vm._watchers.length;
|
||||||
|
vm.createTest = true;
|
||||||
|
const delay = 50;
|
||||||
|
setTimeout(() => {
|
||||||
|
const afterBindCount = vm._watchers.length;
|
||||||
|
expect(afterBindCount).to.be.equal(beforeBindCount + 1);
|
||||||
|
|
||||||
|
vm.createTest = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
const afterDestroyCount = vm._watchers.length;
|
||||||
|
expect(afterDestroyCount).to.be.equal(beforeBindCount);
|
||||||
|
}, delay);
|
||||||
|
}, delay);
|
||||||
|
});
|
||||||
|
});
|
|
@ -49,7 +49,7 @@ exports.createTest = function(Compo, propsData = {}, mounted = false) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发一个事件
|
* 触发一个事件
|
||||||
* mouseenter, mouseleave, mouseover, keyup, change 等
|
* mouseenter, mouseleave, mouseover, keyup, change, click 等
|
||||||
* @param {Element} elm
|
* @param {Element} elm
|
||||||
* @param {EventName} name
|
* @param {EventName} name
|
||||||
* @param {options} opts
|
* @param {options} opts
|
||||||
|
@ -57,7 +57,7 @@ exports.createTest = function(Compo, propsData = {}, mounted = false) {
|
||||||
exports.triggerEvent = function(elm, name, opts) {
|
exports.triggerEvent = function(elm, name, opts) {
|
||||||
let eventName;
|
let eventName;
|
||||||
|
|
||||||
if (/^mouse/.test(name)) {
|
if (/^mouse|click/.test(name)) {
|
||||||
eventName = 'MouseEvents';
|
eventName = 'MouseEvents';
|
||||||
} else if (/^key/.test(name)) {
|
} else if (/^key/.test(name)) {
|
||||||
eventName = 'KeyboardEvent';
|
eventName = 'KeyboardEvent';
|
||||||
|
|
Loading…
Reference in New Issue