Correct way of wrapping javascript class in Angula

2019-02-10 15:05发布

问题:

Inside an AngularJS module I'm developing, I have a Canvas class defined as:

angular.module("myModule", [])
.factory("Canvas", function() {return Canvas;});

var Canvas = function(element, options) {
    this.width = options.width || 300;
    this.height = options.height || 150;
    this.HTMLCanvas = $(element).get(0);
    this.HTMLCanvas.width = canvas.width;
    this.HTMLCanvas.height = canvas.height;
    this.objects = [];
    //Initialize canvas
    this.init();
}
Canvas.prototype.init = function() {/*...*/};
Canvas.prototype.otherMethod = function() {/*...*/};

Now, the Canvas class is never instantiated from inside the module, but rather from an AngularJS controller, like so:

angular.module("myApp.controllers", ["myModule"])
.controller("MainCtrl", ["Canvas", function(Canvas) {
    var canvas = new Canvas("#canvas", {/*options object*/});
    //...
}]);

And so far everything works like a charm. But then I realized that I need the $q service in my canvas object, and since I don't want to resort to injecting it into my controller and then passing it to the Canvas constructor, I thought of modifying my module like so:

angular.module("myModule", [])
.factory("Canvas", ["$q", function(q) {
    var that = this;
    that.q = q;
    return function() {
        Canvas.apply(that, arguments);
    };
}]);

var Canvas = function(element, options) {
    console.log(this.q, element, options);
    this.width = options.width || 300;
    this.height = options.height || 150;
    this.HTMLCanvas = $(element).get(0);
    this.HTMLCanvas.width = canvas.width;
    this.HTMLCanvas.height = canvas.height;
    this.objects = [];
    //Initialize canvas
    this.init();
}
Canvas.prototype.init = function() {/*...*/};
Canvas.prototype.otherMethod = function() {/*...*/};

The initial console.log correctly logs the $q service and the Canvas's original arguments, element and options, but breaks on the call to its init method:

TypeError: undefined is not a function

I suppose that's because this is no longer an instance of Canvas but rather of the anonymous function function(q) {...}.
Any hints on how I can instantiate new Canvas objects with the q property and still retain the class's methods?

EDIT

I've modified my code slightly to give a better idea of what I'd like to achieve:

angular.module("myModule", [])
//.factory("Canvas", function() {return Canvas;})
//.factory("Canvas", ["$q", CanvasFactory])

function CanvasFactory(q) {
    var canvas = this;
    canvas.q = q;
    return function() {
        Canvas.apply(canvas, arguments);
    };
}

var Canvas = function(element, options) {
    console.log(this instanceof Canvas, typeof this.q !== "undefined");
};

If I uncomment the first factory, console.log yields true false, whereas the second factory yields false true. My goal is to get true true, which means that this is in fact an instance of the Canvas class and has the q property defined. Any hint is greatly appreciated.

回答1:

I figured it out:

angular.module("myModule", [])
.factory("Canvas", ["$q", function(q) {
    Canvas.prototype.q = q;
    return Canvas;
}]);

var Canvas = function(element, options) {
    console.log(this instanceof Canvas, typeof this.q !== "undefined");
};

This logs: true true.



回答2:

I create Canvas service like this and is work:

var app = angular.module('myModule', []);

app.factory("Canvas", ["$q", function($q) {

    var Canvas = function(element, options) {
        this.q = $q;

        this.init();
        console.log(this.q, element, options);
    }
    Canvas.prototype.init = function() {/*...*/};
    Canvas.prototype.otherMethod = function() {/*...*/};

    return Canvas;
}]);

app.controller('MainCtrl', ['$scope', 'Canvas', function($scope, Canvas) {
    console.log( new Canvas().q );  
}]);

Also you can see this on Pluncer here