I’m working on an eCommerce application in Angular 2 with a node and express api backend. I’m currently working on the social login section where I am using passport-facebook and jwt for authentication. My code concerns revolve around the Angular 2 portion. When a user clicks on the login/register with facebook button it opens a new tab that begins the facebook login flow. That window will then return the jwt to the opener via window.postMessage. The opener then sends the window a success message and the opened window will close. Everything works correctly but it’s not very clean, i.e. I had to hack it up to make it work. Ideally I would call my authorization service to handle the facebook login instead of handling everything in my component but inside the event handler for the window.postMessage ‘this’ no longer references the component, it references the window object so I can’t call the service via ‘this’. I can’t figure out how to get a reference to the component so I can call the service. Does anyone have any suggestions?
Below is my login component in typescript. I can include other code if need be but I don’t know that it is needed.
import { Component, OnInit, AfterViewChecked } from '@angular/core';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import { ILoginUser } from './loginUser.model';
@Component({
moduleId: module.id,
templateUrl: 'login.component.html',
styleUrls: ['login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private _authService: AuthService, private _router: Router) {
if (window.addEventListener) {
window.addEventListener("message", this.receiveMessage, false);
} else {
(<any>window).attachEvent("onmessage", this.receiveMessage);
}
}
ngOnInit() { }
user: ILoginUser = {
username: "",
password: ""
}
error: boolean = false;
success: boolean = false;
errorMessage: string = "";
redirect: boolean = false;
login() {
this._authService.login(this.user).subscribe(data => {
console.log (data);
if (data.success){
this._router.navigate(['/product']);
} else {
this.error = true,
this.errorMessage = data.message
}
}, errorMsg => {
this.error = true;
this.errorMessage = errorMsg;
});
}
receiveMessage(event: any)
{
if (event.origin !== "http://localhost:3000")
return;
localStorage.setItem("token", event.data.token);
localStorage.setItem("username", event.data.username);
(<any>window).popup.postMessage("success", "http://localhost:3000");
}
ngAfterViewChecked() {
//since we can't call the authservice directly we check to see if a redirect flag is set to true
if (this.redirect) {
this._router.navigate(['/product']);
}
}
authFacebook() {
(<any>window).popup = window.open('http://localhost:3000/api/auth/facebook');
//set the redirect flag to true so when angular checks again we can redirect to the products page
this.redirect = true;
}
}
3
Answers
You should change the way you declare the eventListener (by using
bind(this)
or an anonymous function, or change the way you declare your eventHandlers:bind(this):
anonymous function
different declaration of eventHandler
Take your pick 🙂 With the last option it is easy to detach the eventListener
Combining @PierreDuc’s .bind(this) technique and @Mikeumus’s comment:
You can still directly do this if you prefer:
You can test it by commenting out the origin check and writing this in the developer JavaScript console:
Update the 2nd arg to be the URL of the page you are currently on. This command makes the page think it received a message from itself.
You could also use the @HostListener() function decorator: