Skip to content

Enable to collect inital class properties as vue instance data #29

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 5 commits into from
Nov 13, 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
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -30,18 +32,18 @@ import Component from 'vue-class-component'
<input v-model="msg">
<p>prop: {{propMessage}}</p>
<p>msg: {{msg}}</p>
<p>helloMsg: {{helloMsg}}</p>
<p>computed msg: {{computedMsg}}</p>
<button @click="greet">Greet</button>
</div>
`
})
class App {
// return initial data
data () {
return {
msg: 123
}
}
// initial data
msg = 123

// use prop values for initial data
helloMsg = 'Hello, ' + this.propMessage

// lifecycle hook
mounted () {
Expand Down
13 changes: 4 additions & 9 deletions example/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,8 @@ import Component from '../lib/index'
})
class App extends Vue {
propMessage: string
msg: number

// return initial data
data () {
return {
msg: 123
}
}
msg: number = 123
helloMsg: string = 'Hello, ' + this.propMessage

// lifecycle hook
mounted () {
Expand Down Expand Up @@ -45,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'])
])
Expand All @@ -55,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!' }})
})
9 changes: 9 additions & 0 deletions src/component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Vue from 'vue'
import { VueClass } from './declarations'
import { collectDataFromConstructor } from './data'

const internalHooks = [
'data',
Expand Down Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions src/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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(vm, {
_init: {
get: () => noop
}
})

// 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 => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than whitelisting props above, it might be better to blacklist property keys which should not be collected.

plainData[key] = data[key]
})

return plainData
}
38 changes: 38 additions & 0 deletions test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,44 @@ describe('vue-class-component', () => {
expect(destroyed).to.be.true
})

it('data: should collect from class properties', () => {
@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('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 = ''

Expand Down