Reputation: 4263
I'm trying to use an ES6 class as an Angular service but when it's instantiated, the methods do not have access to the constructor variables.
class dataWrapperService {
constructor($q, $log) {
this.$q = $q;
this.$log = $log;
}
data() {
console.log(this.$q);
}
}
dataWrapperService.$inject = ['$q', '$log'];
app.service('dataWrapper', dataWrapperService);
Once the service gets injected by Angular and I call the data method on it, the method fails to have access to the constructor values.
// calling the data method results in an error
dataWrapper.data(); //TypeError: Cannot read property '$q' of undefined
// console.log output of dataWrapper:
Object
$log: Object
$q: Q(resolver)
__proto__: smDataWrapperService
constructor: smDataWrapperService($q, $log)
data: data()
__proto__: Object
BUT...
I can new the dataWrapperService manually and that works just fine.
var dataWrapper = new smDataWrapperService("hello", "sir");
dataWrapper.data(); // "hello"
What am I missing here?
UPDATE:
This seems to be happening only in promise callbacks:
I usually pass functions to then / catch like this:
$http.get('whatever').then(dataWrapper.data);
But only the following will work:
$http.get('whatever').then((response) => smDataWrapper.data(response))
Upvotes: 8
Views: 4661
Reputation: 11137
With Angular 1.6.4, I'm getting the error when I'm trying to inject a service that is defined as a class in some controller.
Cannot call a class as a function
So instead, I want to use a function as a service. To keep it as clean as possible I would export a function that immediately returns an instantiated anonymous class. And do this in a separate file and import and use that function to create the service in another file.
And since you're asking to use ES6 with Babel, use the babel-plugin-angularjs-annotate to safely inject dependencies by adding '/* @ngInject */'
above the exported function.
The injected services $q
and $log
or available to the class by closure. (No need to pass them in the constructor and assign to this.)
// dataWrapperService.js
/* @ngInject */
export const dataWrapperService = ($q, $log) => new class {
constructor() {
// do stuff
}
data() {
// $q available through closure
console.log($q)
}
}()
// in your.module.js
import angular from 'angular'
import { dataWrapperService } from './dataWrapperService'
export const YourModule = angular
.module('yourmodule', [])
.factory('dataWrapper', dataWrapperService)
.name
Upvotes: 0
Reputation: 705
I know this is late, but maybe somebody will stumble upon this issue like me today with similar problems, so...
Actually, it's not about promises. When you do this:
let function1 = () => {}
let function2 = function1
actually, this
object is different for the functions. Therefore, when you try .then(function1)
, actually function1
is being copied to the param successCallback
and it's this
is changed.
But when you use .then(() => function1())
, successCallback
receives your lambda, and the actual this
of function1
is not lost.
So if you do not know, what you do never assign a function to a function in javascript, use lambdas instead
Upvotes: 2
Reputation: 8970
Angular needs a function at app.factory('dataWrapper', dataWrapperService);
and not a class.
You could add a static factory method to your class and add that to app.factory
.
(see update below code snippet)
The code like this should work:
class dataWrapperService {
constructor($q, $log) {
this.$q = $q;
this.$log = $log;
}
data() {
console.log(this.$q);
}
static dataWrapperFactory($q, $log) {
dataWrapperService.instance = new dataWrapperService($q, $log);
return dataWrapperService.instance;
}
}
dataWrapperService.$inject = ['$q', '$log'];
app.factory('dataWrapper', dataWrapperService.dataWrapperFactory);
Update
So as mentioned in the comments your code should work because an ES6 class is a constructor function and that's what angular.service
is expecting.
I'll check later if I can see any other issue with your code.
Upvotes: 3