I am using Angular 8
Task sounds like this: user opens application in two tabs. User is signed in. And if user signs out in the first tab, then second tab should redirect user to /login.
I am using localstorage to store user's data and storage dom event which triggers when localstorage changes.
Here is my code:
private _redirectIfUserLoggedOut(): void {
window.addEventListener('storage', this._onStorageListener);
}
private _onStorageListener = () => {
const currentUser = localStorage.getItem('__session_token');
if (!currentUser) {
this._router.navigate(['/login']) // this call doesn't work
}
}
The problem is: this._router.navigate doesn't work. Just nothing happens. But when I change this._router.navigate to window.location.href = '/login' redirection works.
this and this._router are not undefined
No errors or warnings in a console.
What is wrong with above code?
The problem is that when the event is triggered, the callback is executed from a different context i.e. from the window. So this in that case would refer to window, and there you don't have the router injected.
You could try to bind your service this to the event listener using:
window.addEventListener('storage', this._onStorageListener.bind(this));
Related
I'm trying to create a very simple chat application in angular using socket io.
I short I have a component that views the chat and service that listen to incoming messages and adds them to the component object to be viewed.
My problem is that I'm passing the conversation object(a reference to the listener so that It can append the message to the object), however, the new message does not appear in the chat component.
Should I change the object into a behavior subject since I reckon the problem is with updating the view?
Please find the code below:
Conversation Service:
listenToIncomingFromAllConversations(conversations: Conversation[]) {
this.webSocketService.listen(this.authService.currentUserValue.id).subscribe(data => {
console.log("The message recieved from the server is:")
const message = data as Message;
var conversation: Conversation = null;
console.log(message);
this.getUserConversations().subscribe(convos => {
/** Refresh conversation and decorate them */
conversations = convos;
this.decorateMessageTabConvos(conversations, convos);
conversation = conversations.find(convo => convo._id == message.conversationID);
console.log("The conversation appear and ready to be append by the new message!");
console.log(conversation);
conversation.otherMemberID = this.getOtherMember(conversation.members);
conversation.messages.push(message);
this.decorateChatBoxMessages(conversation);
});
});
}
Chat Component:
ngOnInit(): void {
this.getConversaitonDecorated();
this.convoService.listenToIncomingFromAllConversations(this.conversations);
}
getConversaitonDecorated() {
this.convoService.getUserConversations().subscribe(convos => {
this.convoService.decorateMessageTabConvos(this.conversations, convos);
});
}
As you can see the listener listens for messages from the server and is initialized in the lifecycle hook of the chat application and the conversation object (the holds all the user's conversation) is passed to the listener for the message to be appended to it.
The message gets appended since it is passed by reference and appears in the console log. However, when I view the specific application that the message was appended to, it is not visible in the view.
Thanks
I think that the problem you have is linked to Angular Change Detection not being triggered by the WebSocket message events.
Angular is not aware of what happens in the browser, and uses a library called Zone.JS, that patches every single native object in the browser (XmlHttpRequest, fetch, settimeout, setinterval, etc.) in order to trigger a change detection every time something happens.
AFAICT Zone.JS doesn't trigger properly every WebSocket method (or the library you are using are loaded before ZoneJS and therefore are invisible to it).
A simple solution is in modifying the WebSocketService, if it's a class under your control, and change the code that publishes the messages to use ngZone.run:
class WebSocketService {
private messages: Subject<Message>;
constructor(private zone: NgZone) {
...
// in your code you will have something like this:
socket.addEventListener('message', (event) => {
// this line runs outside of the Angular zone, so we need:
this.zone.run(() => {
// this code is inside "the Zone", so it will trigger change detection
this.receiveMessage(event);
});
});
}
receiveMessage(message: MessageEvent) {
// do your processing
}
}
Useful links:
NgZone official docs
Some information about disabling patching of WebSocket entirely
This question is about accessing native versions of some methods, but in my answer there is a sample about how to disable patching of WebSocket prototype.
For now I'm doing like this:
firebase.auth().onAuthStateChanged((user) => {
if (!user.emailVerified) {
//Something
}
});
But, the problem is I have added this block of code in multiple pages and whenever the authentication changed (user logged in or logged out) It calls all those functions and that effects other code!
So, is there any way by which we get if users email is verified or not - without using 'onAuthStateChanged()'
Anywhere in your page you can do:
firebase.auth().currentUser.emailVerified
To determine the current state. You only need onAuthStateChanged() to monitor when the state changes.
listener
onIdTokenChanged
to reload
auth.currentUser.reload()
auth.currentUser.getIdToken(true)
So I'm trying to update a list view in a parent tab window using Angular.js with the Ionic Framework, but I can't quite seem to figure out how to do it.
Here is the code for the child window:
$scope.currentUsername = Parse.User.current().get("username");
$scope.saveChanges = function saveChanges(user){
var currentUser = Parse.User.current();
var newUserName = user.newUsername;
currentUser.set("username", newUserName);
currentUser.save(null, {
success: function(currentUser){
alert("Changes successfully made!");
}
});
$state.go('tab.more');
}
$scope.cancelChanges = function cancelChanges(){
$state.go('tab.more');
}
And I am trying to take the data once it's sent to Parse back to the parent view but I don't know how to refresh the page after the script is run. In my function cancelChanges() I simply used $state.go to go back to the parent view because why update the view when there is not data to be updated.
Here is the code for my parent window:
$scope.currentUser = Parse.User.current().get("username");
$scope.editProfile = function editProfile(){
$state.go('tab.more-editusername');
}
$scope.logOut = function logOut(){
Parse.User.logOut();
alert("Logout successful!");
$state.go('login');
}
ionic views are cached by default... so I do not believe the update is happening unless you force it to
From Documentation
View LifeCycle and Events Views can be cached, which means controllers normally only load once, which may affect your controller
logic. To know when a view has entered or left, events have been added
that are emitted from the view's scope. These events also contain data
about the view, such as the title and whether the back button should
show. Also contained is transition data, such as the transition type
and direction that will be or was used.
you can try and listen for the $ionicView.enter event and then update the view with the new data
So this is my problem.
I can successfully login from my angularJS app using the auth factory i made that communicates to my pp rest API.
lets say Auth.login(user) -> (POST) myapi.com/user/login: the response is the user object that Auth saves locally. Thus Auth.getCurrentUser() returns local user object.
my rest API, i also have a myapi.com/user/get_loggedin_user which returns the current logged in user (using the php session). So if Auth.getCurrentUser should actually check if local user exists, if not do an ajax to myapi.com/user/get_loggedin_user and check if logged in before responding with null. One problem here is, ajax is annoying like this, you would then need to put in a success callback function and have all your code execute inside the success callback.
Now lets say im on the Angular App (website), mydomain.com/user/dashboard (already logged in), and then i refresh my browser. Now, when the page reloads, angular does not know my current user, so before it redirects me to mydomain/login, i want it to check if the user is logged in. i can obviously do a 1 time call within the controller, but is there a more easy way where i can register within a controller with some access restrictions (Eg: logged_in == true), and when you visit any page with logged in requirement, it checks local user (gets the user if does not exist), and redirects to login page if null, or display the page once it matches the requirements?
Different common page requirements: null, logged_in, admin, function: haveAccess(user, object).
NOTE: im using stateProvider
If I understood your question correctly, you are asking about how to check whether the user is logged in before the controller is invoked, and to avoid the check for a logged-in status in each controller that needs it.
If so, you should look into the resolve parameter - this exists both in $routerProvider and $stateProvide.
Essentially you could "resolve" your loggedInUser variable (by doing whatever you need to do via your MyAuth service.
Here's an example of what I mean with $routeProvider:
$routeProvider
.when("/someSecuredContent", {
templateUrl: 'someSecuredContent.html',
controller: 'SecuredController',
resolve: {
loggedInUser: function(MyAuth){
return MyAuth.loggedIn(); // MyAuth.loggedIn() should return a $q promise
}
}
});
Then in the controller, loggedInUser will be injected.
Here's a site with more examples.
Correct me if im wrong:
Do this within the Main Controller (make sure you inject the dependancies like rootScope, state, and your own Authfactory)
$rootScope.$on('$stateChangeStart', function (event, next, toParams) {
if (needToBeLoggedIn()) { //use the next object to read any data, and you can set on the state some flag
event.preventDefault()
MyAuth.loggedIn(function success(){ $state.go(next,toParams); }, function err (){/*send somewhere else*/});
}
})
Put logged_in = true to cookieStore in your login method after authentication as below.
$cookieStore.put('logged_in',true);
$rootScope.logged_in = true;
and in your Controller, do
$rootScope.logged_in = $cookieStore.get('logged_in');
Now you can use this logged_in variable anywhere in the UI to check if the user is logged in.
Make sure to use 'ngCookies' module in your app. and pass the $cookieStore dependency to your controller. You can even keep the user object itself similar to logged_in variable in cookies and retrieve it from cookies.
Make sure to do logged_in = false and clear other variables in cookies and set it to blank in your logout method.
In my app I use accounts-github. Works perfect, but I have one problem.
In one of my templates I do
Template.bar.rendered = function () {
if (Meteor.user()) {
// setup stuff
}
}
The problem is that if the user initially is not logged in this code is not executed (thats ok). But when the user authenticates this code is not executed again. So the question is how can I listen for this change inside a template (doesn't have to be in inside the rendered function!)?
You could use Deps.autorun. (http://docs.meteor.com/#deps_autorun)
Usually Deps.autorun would run for your whole Meteor app. If you want to make it so that it only runs per template you would need to create and stop it in the rendered and destroyed template callbacks
e.g
var loginRun;
Template.bar.rendered = function() {
loginRun = Deps.autorun(function() {
if(Meteor.user()) {
//Stuff to run when logged in
}
});
}
Template.bar.destroyed = function() {
loginRun.stop();
}
If you don't need it to run per template (need it to run just once for you app on any template, then you can use the Deps.autorun on its own, anywhere in your client side code.
Meteor.user() is reactive, it would ensure that the Deps.autorun callback runs again when it changes, so you could theoretically use it to do things when the user logs in or out.
Other alternatives is there is a package on atmosphere that provides login and logout hooks, though they basically would use the Deps.autorun like above to work anyway. See https://github.com/BenjaminRH/meteor-event-hooks
My solution for similar problem was to
Attach an event to template where the login happens
Re-render template if login is succesful so the Template.bar.rendered is called
E.g.
Template.bar.events({
'click .loginButton' : function() {
if( Meteor.call.Login( username, pw ) )
{
$('#bar').html( Meteor.render( Template.bar ));
//jQuery is optional
}
});