Skip to content

Provide a helper to create custom decorators #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vue>) => void)[] = []

export function componentFactory (
Component: VueClass,
options: Vue.ComponentOptions<any> = {}
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions src/data.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { VueClass } from './declarations'

import { componentFactory } from './component'

export { createDecorator } from './util'

export default function Component <U extends Vue>(options: Vue.ComponentOptions<U>): <V extends VueClass>(target: V) => V
export default function Component <V extends VueClass>(target: V): V
export default function Component <V extends VueClass>(options: Vue.ComponentOptions<any> | V): any {
Expand Down
21 changes: 21 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as Vue from 'vue'
import { $decoratorQueue } from './component'

export const noop = () => {}

export function createDecorator (
factory: (options: Vue.ComponentOptions<Vue>, key: string) => void
): (target: Vue, key: string) => void
export function createDecorator (
factory: (options: Vue.ComponentOptions<Vue>, key: string, index: number) => void
): (target: Vue, key: string, index: number) => void
export function createDecorator (
factory: (options: Vue.ComponentOptions<Vue>, 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))
}
}
34 changes: 33 additions & 1 deletion test/test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Component from '../lib/index'
import Component, { createDecorator } from '../lib/index'
import { expect } from 'chai'
import * as Vue from 'vue'

Expand Down Expand Up @@ -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<Vue> = 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
})
})