How to Easily Bind VueJS to a Typescript or ES6 Class





5.00/5 (2 votes)
How to easily bind VueJS to a Typescript or ES6 class
Like many, I have been very impressed with the work done on VueJS and TypeScript and naturally, I want to leverage the benefits of each in my projects. Unfortunately, the Vue configuration model does not facilitate binding to a class as such. Instead, Vue asks for a bag of methods & properties via its constructor:
var app = new Vue({
el: document.getElementById('app'),
data: {counter: 1},
methods: {
increment: function(){
this.counter ++;
}
}
});
But this is not the way modern JavaScript applications are written:
- JavaScript components are encapsulated in classes - now supported natively in ES6 and of course in Typescript
- Regardless of how you build your components, they should be agnostic towards any UI binding (if any) being employed against them
So, for example, the above component would be encapsulated in the following Typescript module:
export class Timer {
public counter: number = 0;
public increment(): void {
this.counter ++;
}
}
It would be nice if Vue would let you do something like:
new Vue({el: xyz, model: new Timer()})
But of course, it doesn't, and hence why you are reading this post. Well, we had this problem, but with a little determination, we wrote this method which maps any class into a Vue instance. We've been using it for a few months now and haven't noticed any departures from normal Vue behaviours. I've self-documented inline below. Please grab and use/modify as you wish:
// Vue requires an array of functions/methods to pass to its 'methods' property,
// so we create an empty list here and populate below using reflection
let functions: any = {};
/*
fn_bindFunctions
Helper routine to iterate up the inheritance model, binding properties/functions as required.
For example, if you are binding Car, which inherits from Vehicle, this will
make sure that Vue has access to the properties in Vehicle
*/
let fn_bindFunctions = (obj: any) => {
// Get all the properties and functions etc of this object
let fnList = Object.getOwnPropertyNames(obj);
// Iterate through each property/function we have found
fnList.map((propertyName: string) => {
if (typeof (propertyName) !== "string") return;
if (propertyName === "constructor") return;
// Map the function
if ((this as any)[propertyName] instanceof Function) {
// Break if we've already got this function name
// (i.e., an inherited class has overridden it)
if (typeof (functions[propertyName]) !== "undefined") return;
// Map to our VUE object
functions[propertyName] = (...args: any[]) => {
// By using a closure and referring to 'this'
// we change the JavaScript context of the event handler
// from 'vue', back to this actual controller!
(this as any)[propertyName](...args);
};
}
});
// Bind parent object (ie. the class that this object inherits from) -
// note that BaseController is the name of our base class, from which all objects inherit
if (typeof (obj.constructor) !== "undefined" &&
typeof (obj.constructor.name) === "string" &&
obj.constructor.name !== "BaseController") {
fn_bindFunctions(Object.getPrototypeOf(obj));
}
};
// Kick off function binding with the current instance
fn_bindFunctions(Object.getPrototypeOf(this));
// Create our vue bindings
var app = new Vue({
el: document.getElementById('app'),
data: this, // Binding the data to 'this' works well
methods: functions // Bind the methods to the array of functions we created above
});