From 6fa91fc272f0545d6a7aa92210bb1ebdefe25acf Mon Sep 17 00:00:00 2001 From: ktsn Date: Fri, 11 Nov 2016 14:13:39 +0900 Subject: [PATCH 1/2] add create decorator helper --- src/component.ts | 10 ++++++++++ src/data.ts | 3 +-- src/index.ts | 2 ++ src/util.ts | 21 +++++++++++++++++++++ test/test.ts | 34 +++++++++++++++++++++++++++++++++- 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/util.ts diff --git a/src/component.ts b/src/component.ts index 1861d79..cf260f3 100644 --- a/src/component.ts +++ b/src/component.ts @@ -17,6 +17,11 @@ const internalHooks = [ 'render' ] +// Property, method and parameter decorators created by `createDecorator` helper +// will enqueue functions that update component options for lazy processing. +// They will be executed just before creating component constructor. +export let $decoratorQueue: ((options: Vue.ComponentOptions) => void)[] = [] + export function componentFactory ( Component: VueClass, options: Vue.ComponentOptions = {} @@ -53,6 +58,11 @@ export function componentFactory ( } }) + // decorate options + $decoratorQueue.forEach(fn => fn(options)) + // reset for other component decoration + $decoratorQueue = [] + // find super const superProto = Object.getPrototypeOf(Component.prototype) const Super: VueClass = superProto instanceof Vue diff --git a/src/data.ts b/src/data.ts index b7fda12..9180524 100644 --- a/src/data.ts +++ b/src/data.ts @@ -1,7 +1,6 @@ import * as Vue from 'vue' import { VueClass } from './declarations' - -const noop = () => {} +import { noop } from './util' export function collectDataFromConstructor (vm: Vue, Component: VueClass) { // Create dummy Vue instance to collect diff --git a/src/index.ts b/src/index.ts index 2e1096f..f1cdfa8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,8 @@ import { VueClass } from './declarations' import { componentFactory } from './component' +export { createDecorator } from './util' + export default function Component (options: Vue.ComponentOptions): (target: V) => V export default function Component (target: V): V export default function Component (options: Vue.ComponentOptions | V): any { diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..385230b --- /dev/null +++ b/src/util.ts @@ -0,0 +1,21 @@ +import * as Vue from 'vue' +import { $decoratorQueue } from './component' + +export const noop = () => {} + +export function createDecorator ( + factory: (options: Vue.ComponentOptions, key: string) => void +): (target: Vue, key: string) => void +export function createDecorator ( + factory: (options: Vue.ComponentOptions, key: string, index: number) => void +): (target: Vue, key: string, index: number) => void +export function createDecorator ( + factory: (options: Vue.ComponentOptions, key: string, index: number) => void +): (target: Vue, key: string, index: any) => void { + return (_, key, index) => { + if (typeof index !== 'number') { + index = undefined + } + $decoratorQueue.push(options => factory(options, key, index)) + } +} diff --git a/test/test.ts b/test/test.ts index d778a6b..00e2e8c 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,4 +1,4 @@ -import Component from '../lib/index' +import Component, { createDecorator } from '../lib/index' import { expect } from 'chai' import * as Vue from 'vue' @@ -143,4 +143,36 @@ describe('vue-class-component', () => { expect(a.a).to.equal(1) expect(a.b).to.equal(2) }) + + it('createDecorator', function () { + const Prop = createDecorator((options, key) => { + // component options should be passed to the callback + // and update for the options affect the component + (options.props || (options.props = {}))[key] = true + }) + + const NoCache = createDecorator((options, key) => { + // options should have computed and methods etc. + // that specified by class property accessors and methods + const computedOption: Vue.ComputedOptions = options.computed![key] + computedOption.cache = false + }) + + @Component + class MyComp extends Vue { + @Prop foo: string + @NoCache get bar (): string { + return 'world' + } + } + + const c = new MyComp({ + propsData: { + foo: 'hello' + } + }) + expect(c.foo).to.equal('hello') + expect(c.bar).to.equal('world') + expect((MyComp as any).options.computed.bar.cache).to.be.false + }) }) From 967d8b2d18dd51cbc000407b76d665f18b13cb26 Mon Sep 17 00:00:00 2001 From: ktsn Date: Sat, 12 Nov 2016 14:44:22 +0900 Subject: [PATCH 2/2] update readme for createDecorator helper --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index d2f1e52..019cdfd 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,40 @@ class App { You may also want to check out the `@prop` and `@watch` decorators provided by [vue-property-decorators](https://github.com/kaorun343/vue-property-decorator). +### Create Custom Decorators + +You can extend the functionality of this library by creating your own decorators. vue-class-component provides `createDecorator` helper to create custom decorators. `createDecorator` expects a callback function as the 1st argument and the callback will receive following arguments: + +- `options`: Vue component options object. Changes for this object will affect the provided component. +- `key`: The property or method key that the decorator is applied. +- `parameterIndex`: The index of a decorated argument if the custom decorator is used for an argument. + +Example of creating `NoCache` decorator: + +``` js +// decorators.js +import { createDecorator } from 'vue-class-component' + +export const NoCache = createDecorator((options, key) => { + // component options should be passed to the callback + // and update for the options object affect the component + options.computed[key].cache = false +}) +``` + +``` js +import { NoCache } from './decorators' + +@Component +class MyComp extends Vue { + // the computed property will not be cached + @NoCache + get random () { + return Math.random() + } +} +``` + ### Build the Example ``` bash