Reputation: 7328
I am using TypeScript with Angular JS to write a common service which will handle the POST and GET Requests to a server.
This is what the service looks like:
module TBApp {
export class apiService {
static $inject = ['$http', 'notificationService'];
constructor(private $http, private notificationService: notificationService) {
}
get(url, config, success, failure) {
return this.$http.get(url, config)
.then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
}
post(url, data, success, failure) {
return this.$http.post(url, data)
.then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
}
handleResponse(result, success) {
this.notificationService.displaySuccess(result.data.message);
success(result);
}
handleError(result, failure) {
if (result.status === '401') {
this.notificationService.displayError('Authentication required.');
//TODO: redirect to login page
}
else if (failure !== null) {
failure(result);
}
}
}
}
so in the controller shown below I want to call loginCompleted
on login success.
module TBApp {
export class loginController extends MyApp.BaseController {
membershipService: membershipService;
apiService: apiService;
static $inject = ['$scope', 'membershipService', 'apiService', '$location'];
constructor($scope, membershipService: membershipService, apiService: apiService, private $location) {
super($scope);
this.scope.user = new User(null, null);
this.membershipService = membershipService;
this.apiService = apiService;
}
login() {
// HERE: this.membershipService Is NOT NULL OR UNDEFINED
console.log(this.membershipService);
this.apiService.post('/api/account/authenticate', this.scope.user, this.loginCompleted, this.loginFailed);
}
loginCompleted(response) {
//This method will save the logged in user to cookies
//HERE : this.membershipService Is UNDEFINED
this.membershipService.saveCredentials(this.scope.user);
// redirect to home page
this.$location.path('/');
}
loginFailed(response) {
alert('login failed');
console.log(response);
}
}
}
The function gets called everything is working except for the this.membershipService
is undefined inside the loginCompleted()
function.
I think this is because the loginCompleted()
function is called from inside the apiService
, how can I fix it? what am i doing wrong?
Upvotes: 0
Views: 1309
Reputation: 7630
@Dawood, you are losing context, my friend. This problem caused because you send functions (loginCompleted, loginFailed) as params, and while they executed their key word this
linked to another object.
What you can do here - you can send that functions with binded context (it will be setted always to object which you define as first argument).
module TBApp {
export class loginController extends MyApp.BaseController {
static $inject: string[] = ['$scope', 'membershipService', 'apiService', '$location'];
constructor(
private $scope: ng.IScope,
private membershipService: membershipService,
private apiService: apiService,
private $location
) {
super($scope);
this.scope.user = new User(null, null);
}
login() {
// HERE: this.membershipService Is NOT NULL OR UNDEFINED
console.log(this.membershipService);
this.apiService.post('/api/account/authenticate', this.scope.user, this.loginCompleted.bind(this), this.loginFailed.bind(this));
}
loginCompleted(response) {
//This method will save the logged in user to cookies
//HERE : this.membershipService Is UNDEFINED
this.membershipService.saveCredentials(this.scope.user);
// redirect to home page
this.$location.path('/');
}
loginFailed(response) {
alert('login failed');
console.log(response);
}
}
}
In this case you send functions with binded context:
this.loginCompleted.bind(this)
this.loginFailed.bind(this)
Two new functions will be created with binded context to current class.
More information about this subject: understanding context in js
P.S. Don't blame me I changed code style a bit. Hope my answer will help
UPDATED code below related to comments conversation
module TBApp {
export class loginController extends MyApp.BaseController {
static $inject: string[] = ['$scope', 'membershipService', 'apiService', '$location'];
constructor(
private $scope: ng.IScope,
private membershipService: membershipService,
private apiService: apiService,
private $location
) {
super($scope);
this.scope.user = new User(null, null);
}
login() {
// HERE: this.membershipService Is NOT NULL OR UNDEFINED
console.log(this.membershipService);
this.apiService.post('/api/account/authenticate', this.scope.user,
(response) => {
this.membershipService.saveCredentials(this.scope.user);
// redirect to home page
this.$location.path('/');
},
(response) => {
alert('login failed');
console.log(response);
});
}
}
}
Upvotes: 2
Reputation: 222494
It should be
this.apiService.post(
'/api/account/authenticate',
this.scope.user,
this.loginCompleted.bind(this), this.loginFailed.bind(this)
)
Alternatively,
this.loginCompleted = this.loginCompleted.bind(this)
this.loginFailed = this.loginFailed.bind(this)
can be done in constructor for all callback/handler methods
Upvotes: 2