From 0a5c8452840040d43782148bbf2d4d8c10e87021 Mon Sep 17 00:00:00 2001 From: ktsn Date: Mon, 7 Nov 2016 23:20:50 +0900 Subject: [PATCH 1/5] collect inital class properties as vue instance data --- src/component.ts | 9 +++++++++ src/data.ts | 36 ++++++++++++++++++++++++++++++++++++ test/test.ts | 19 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/data.ts diff --git a/src/component.ts b/src/component.ts index 22e737b..1861d79 100644 --- a/src/component.ts +++ b/src/component.ts @@ -1,5 +1,6 @@ import * as Vue from 'vue' import { VueClass } from './declarations' +import { collectDataFromConstructor } from './data' const internalHooks = [ 'data', @@ -44,6 +45,14 @@ export function componentFactory ( } } }) + + // add data hook to collect class properties as Vue instance's data + ;(options.mixins || (options.mixins = [])).push({ + data (this: Vue) { + return collectDataFromConstructor(this, Component) + } + }) + // find super const superProto = Object.getPrototypeOf(Component.prototype) const Super: VueClass = superProto instanceof Vue diff --git a/src/data.ts b/src/data.ts new file mode 100644 index 0000000..ad6a4b2 --- /dev/null +++ b/src/data.ts @@ -0,0 +1,36 @@ +import * as Vue from 'vue' +import { VueClass } from './declarations' + +const noop = () => {} + +export function collectDataFromConstructor (vm: Vue, Component: VueClass) { + // Create dummy Vue instance to collect + // initial class properties from the component constructor. + // To prevent to print warning, + // the data object should inherit Vue.prototype. + const data = Object.create(Component.prototype, { + _init: { + get: () => noop + } + }) + + // proxy to each prop values + const propKeys = Object.keys(vm.$options.props || {}) + propKeys.forEach(key => { + Object.defineProperty(data, key, { + get: () => vm[key] + }) + }) + + // call constructor with passing dummy instance + // `data` object will have initial data + Component.call(data) + + // create plain data object + const plainData = {} + Object.keys(data).forEach(key => { + plainData[key] = data[key] + }) + + return plainData +} \ No newline at end of file diff --git a/test/test.ts b/test/test.ts index fe26222..12f7389 100644 --- a/test/test.ts +++ b/test/test.ts @@ -25,6 +25,25 @@ describe('vue-class-component', () => { expect(destroyed).to.be.true }) + it('data', () => { + @Component({ + props: ['foo'] + }) + class MyComp extends Vue { + foo: number + a: string = 'hello' + b: number = this.foo + 1 + } + + const c = new MyComp({ + propsData: { + foo: 1 + } + }) + expect(c.a).to.equal('hello') + expect(c.b).to.equal(2) + }) + it('methods', () => { let msg: string = '' From 374155e6cc98ba9deac7666fe4a2e326d5e9e98a Mon Sep 17 00:00:00 2001 From: ktsn Date: Tue, 8 Nov 2016 00:19:23 +0900 Subject: [PATCH 2/5] update example --- example/example.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/example/example.ts b/example/example.ts index 99bb431..86d35d2 100644 --- a/example/example.ts +++ b/example/example.ts @@ -8,14 +8,7 @@ import Component from '../lib/index' }) class App extends Vue { propMessage: string - msg: number - - // return initial data - data () { - return { - msg: 123 - } - } + msg: number = 123 // lifecycle hook mounted () { From 19600ac3d17e63883d168a4c548595e4292471cc Mon Sep 17 00:00:00 2001 From: ktsn Date: Tue, 8 Nov 2016 01:15:35 +0900 Subject: [PATCH 3/5] update readme to describe data can be declared as class properties --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cf1f606..e133c6e 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,11 @@ Note: 2. Computed properties can be declared as class property accessors. -3. `data`, `render` and all Vue lifecycle hooks can be directly declared as class member methods as well, but you cannot invoke them on the instance itself. When declaring custom methods, you should avoid these reserved names. +3. Initial `data` can be declared as class properties ([babel-plugin-transform-class-properties](https://babeljs.io/docs/plugins/transform-class-properties/) is required if you use Babel). -4. For all other options, pass them to the decorator function. +4. `data`, `render` and all Vue lifecycle hooks can be directly declared as class member methods as well, but you cannot invoke them on the instance itself. When declaring custom methods, you should avoid these reserved names. + +5. For all other options, pass them to the decorator function. ``` js import Component from 'vue-class-component' @@ -36,12 +38,8 @@ import Component from 'vue-class-component' ` }) class App { - // return initial data - data () { - return { - msg: 123 - } - } + // initial data + msg = 123 // lifecycle hook mounted () { From 1af226cf302ce592bd0d8a213ef14ae5cccd89e3 Mon Sep 17 00:00:00 2001 From: ktsn Date: Sat, 12 Nov 2016 11:35:35 +0900 Subject: [PATCH 4/5] add class property example using props value --- README.md | 4 ++++ example/example.ts | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e133c6e..d2f1e52 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ import Component from 'vue-class-component'

prop: {{propMessage}}

msg: {{msg}}

+

helloMsg: {{helloMsg}}

computed msg: {{computedMsg}}

@@ -41,6 +42,9 @@ class App { // initial data msg = 123 + // use prop values for initial data + helloMsg = 'Hello, ' + this.propMessage + // lifecycle hook mounted () { this.greet() diff --git a/example/example.ts b/example/example.ts index 86d35d2..5affdb4 100644 --- a/example/example.ts +++ b/example/example.ts @@ -9,6 +9,7 @@ import Component from '../lib/index' class App extends Vue { propMessage: string msg: number = 123 + helloMsg: string = 'Hello, ' + this.propMessage // lifecycle hook mounted () { @@ -38,6 +39,7 @@ class App extends Vue { }), h('p', ['prop: ', this.propMessage]), h('p', ['msg: ', this.msg]), + h('p', ['helloMsg: ', this.helloMsg]), h('p', ['computed msg: ', this.computedMsg]), h('button', { on: { click: this.greet }}, ['Greet']) ]) @@ -48,5 +50,5 @@ class App extends Vue { // mount new App({ el: '#el', - render: h => h(App, { props: { propMessage: 'Hello!' }}) + render: h => h(App, { props: { propMessage: 'World!' }}) }) From 0dd305e0825ae9de678c96af26260f3537bbafd3 Mon Sep 17 00:00:00 2001 From: ktsn Date: Sun, 13 Nov 2016 21:34:31 +0900 Subject: [PATCH 5/5] use vm instance as prototype instead of Component.prototype --- src/data.ts | 10 +--------- test/test.ts | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/data.ts b/src/data.ts index ad6a4b2..b7fda12 100644 --- a/src/data.ts +++ b/src/data.ts @@ -8,20 +8,12 @@ export function collectDataFromConstructor (vm: Vue, Component: VueClass) { // initial class properties from the component constructor. // To prevent to print warning, // the data object should inherit Vue.prototype. - const data = Object.create(Component.prototype, { + const data = Object.create(vm, { _init: { get: () => noop } }) - // proxy to each prop values - const propKeys = Object.keys(vm.$options.props || {}) - propKeys.forEach(key => { - Object.defineProperty(data, key, { - get: () => vm[key] - }) - }) - // call constructor with passing dummy instance // `data` object will have initial data Component.call(data) diff --git a/test/test.ts b/test/test.ts index 12f7389..d778a6b 100644 --- a/test/test.ts +++ b/test/test.ts @@ -25,7 +25,7 @@ describe('vue-class-component', () => { expect(destroyed).to.be.true }) - it('data', () => { + it('data: should collect from class properties', () => { @Component({ props: ['foo'] }) @@ -44,6 +44,25 @@ describe('vue-class-component', () => { expect(c.b).to.equal(2) }) + it('data: should collect custom property defined on beforeCreate', () => { + @Component + class MyComp extends Vue { + $store: any + foo: string = 'Hello, ' + this.$store.state.msg + + beforeCreate () { + this.$store = { + state: { + msg: 'world' + } + } + } + } + + const c = new MyComp() + expect(c.foo).to.equal('Hello, world') + }) + it('methods', () => { let msg: string = ''