I'm currently building an app in Polymer which uses Firebase Authentication to login with your Google+ account or Email. When you run the app it will first check if the user already has a session. If not then the user is being routed to the login page which will display a login form and will handle the login by using <firebase-auth> by Polymerfire. The login page will notify the user property upwards to the my-app (app shell). If the user has been successfully authenticated we send them to the homepage my-app. This will fire the routePageChanged observer in my-app.
_routePageChanged(page) {
if(this.user && this.user.email && this.user.refreshToken) {
this.set("noUser", false);
this.page = page
} else {
this.set("noUser", true);
this.page = 'login';
}
}
Okay, so far so good. If we route a user to the login form and let them authenticate, all is working fine. However, if the user already has a session the routePageChanged observer in my-app will fire and won't have a user object yet.
Problem 1
The problem in the example above is that it'll always show the login page for a few miliseconds. We can fix this by adding a timeout (?) on the initial route and show a spinner and wait for the firebase authentication. This seems a little bit hacky as I don't know how long it will take for the user to automatically authenticate? What is the best way to wait for the authentication and then do a route?
Problem 2
In the app-login page I'm handling the login. I've chosen to use a singInWithRedirect as I want the login to be available on mobile (popups are being blocked sometimes). The problem with singInWithRedirect is that it'll also re-render/initialise the whole app after authentication. This means that after creating a valid auth session the routePageChanged observer in my-app will be fired and there won't be a user object for a few miliseconds, which will make the app route to /login.
The _userChanged function in app-login will then route back to the overview on its turn. This will also show the login page for a few miliseconds.
<firebase-auth id="auth" user="{{user}}" provider="{{provider}}" on-error="handleError" on-user-changed="_userChanged"></firebase-auth>
_userChanged(event) {
if (event.detail.value.uid) {
this.set("waitingForAuthChanged", false);
this.set("user", event.detail.value);
window.history.pushState({}, null, "/overview");
window.dispatchEvent(new CustomEvent('location-changed'));
}
}
What's the best way to properly handle all these states? I used to use the signInWithPopup function, this won't initialise the app after authentication. I'm trying to achieve the same with the redirect.
You use routePageChanged observer instead of user object observer. So try to manage all with observer of user:
<firebase-auth id="auth" user="{{user}}" provider="{{provider}}" on-error="handleError" on-user-changed="_userChanged"></firebase-auth>
...
<iron-pages role="main" selected="[[page]]" attr-for-selected="name" selected-attribute="visible" fallback-selection="404" >
<div name="home"><my-home>Home Page</my-home></div>
<div name="signin"><signin-page>Signing page</signin-page></div>
<div name="404"></div>
</iron-pages>
...
<script>
...
static get observers() {
return ['_isUserLoggedIn(user)']
...
_isUserLoggedIn(u) {
if (u) { this.set('signedIn', true);
this.set('page', 'home'); // user signed in, then go home
} else {
this.set('signedIn', false);
this.set('page', 'signin'); // user not signed in, so go sign in page
}
}
</script>
Demo
EDIT: It appears my change detection and lifecycle breaks entirely after navigation to a different component. Why?
I'm having some strange problems with#ngrx/store on nativescript and nativescript map based plugins for google maps and mapbox. Code is correct, maps load perfect with data(markers set) but having issues with subscribing or navigating.
I'ved tried both and they work perfect until I attempt navigation;
With google maps:
Navigation and subscribing to data in other pages works perfect until I try to navigate back/forward again to the maps. I always get an error on google map's onReady method.
With MapBox:
navigation works fine including going back to maps. However, my async pipe fails to actually populate other page data until I navigate back to the original map component!!! I assume the subscriber don't get triggered upon navigating. I managed to get it almost working if I don't unsubscribe with ngOnDestroy() but this sends old or wrong data obviously.
Here is code
Map page(first component):
ngOnDestroy() {
this.subscription.unsubscribe();
}
this is the mapbox code but it's similar for googlemaps, executes with map is loaded and add markers(no problems here on both googlemaps or mapbox).
onMapReady(args) {
let mapMarkers = [];
this.subscription = this.store
.select('mainData')
.subscribe((data: any) => {
if (data !== null) {
this.markers = data.markers.map((mark) => {
return {
lat: mark.venue.lat,
lng: mark.venue.lon,
iconPath: this.iconMaker(mark.group, mark.sport),
userData: mark,
onTap: (marker) => {
let urlExt = "/event/" + mark.id; this.routerExtensions.navigate([urlExt]);
},
}
});
args.map.addMarkers(this.markers);
}
});
When I tap on a map marker, it navigates to this second page(event/:id) that displays the event data related to the map marker.
Event Component
Html:
<StackLayout *ngFor=" let model of models |async" orientation="vertical">
<StackLayout orientation="horizontal" style="padding: 3">
<Label class="h2" textWrap="true" text="Venue: "></Label>
<Label class="h2" textWrap="true" [text]="model.venue.name"></Label>
</StackLayout>
...
Component:
ngOnInit() {
this.route.params
.forEach((params) => {
this.id = +params['id'];
console.dir("Found match" + this.id);
if (params['id']) {
used async pipe to send data to html. In google maps this works perfect, in mapbox it doesn't trigger until I attempt to navigate away. I also attempted to just subscribe to the returned Observable but still same outcome in MapBox;Html not waiting for async loads fine.
this.models = this.mapService.getEvent(this.id);
});
}
});
}
This all works 100% perfect in google maps except I can't navigate back to my map component without it instantly crashing.
I would love for either to work.
I do get lots of errors
for unlinking rxjs module leading me to believe that might be an issue:
02-07 14:29:59.523 24939 24939 W System.err: remove failed: EACCES (Permission denied) : /data/local/tmp/org.nativescript.pickn/sync/tns_modules/rxjs/src/MiscJSDoc.ts
02-07 14:29:59.543 5475 5475 E audit : type=1400 msg=audit(1486499399.523:327875): avc: denied { unlink } for pid=24939 comm="ivescript.pickn" name="MiscJSDoc.ts" dev="sda17" ino=463318 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:shell_data_file:s0 tclass=file permissive=0
02-07 14:29:59.573 24939 24939 W System.err: remove failed: EACCES (Permission denied) : /data/local/tmp/org.nativescript.pickn/sync/tns_modules/rxjs/src/observable/dom/MiscJSDoc.ts
02-07 14:29:59.593 5475 5475 E audit : type=1400 msg=audit(1486499399.573:328068): avc: denied { unlink } for pid=24939 comm="ivescript.pickn" name="MiscJSDoc.ts" dev="sda17" ino=463540 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:shell_data_file:s0 tclass=file permissive=0
AFAIK, with ngrx, the subscription to your store should probably only happen once in your component, instead of every time the onMapReady(args) method is fired, which maybe happening when you navigate back to the map - verify this first by adding a console.log to the onMapReady(args) method.
From what I've seen (now) ngrx subscriptions belong in the constructor method and console.log has helped me understand the life-cycle of numerous components now... :-)
One suggestion might be to:
add a component flag called isMapboxReady
toggle it in the onMapReady method
move the subscription out into the constructor and add a check inside the subscription for if the isMapboxReadyflag is true before adding markers.
After deleting /serviceworker.js from my root directory, Chrome still runs the service worker that I removed from my webroot. How do I uninstall the service worker from my website and Chrome so I can log back into my website?
I've tracked the issue down to Service Work's cache mechanism and I just want to remove for now until I have time to debug it. The login script that I'm using redirects to Google's servers for them to login to their Google account. But all I get from the login.php page is an ERR_FAILED message.
Removing Service Workers Programmatically:
You can remove service workers programmatically like this:
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for(let registration of registrations) {
registration.unregister();
}
});
Docs: getRegistrations, unregister
Removing Service Workers Through The User Interface
You can also remove service workers under the Application tab in Chrome Devtools.
You can also go to the URL: chrome://serviceworker-internals/ and unregister a serviceworker from there.
You can do this through Chrome Developer Tool as well as Programatically.
Find all running instance or service worker by typing
chrome://serviceworker-internals/
in a new tab and then select the serviceworker you want to unregister.
Open Developer Tools (F12) and Select Application. Then Either
Select Clear Storage -> Unregister service worker
or
Select Service Workers -> Choose Update on Reload
Programatically
if(window.navigator && navigator.serviceWorker) {
navigator.serviceWorker.getRegistrations()
.then(function(registrations) {
for(let registration of registrations) {
registration.unregister();
}
});
}
In Google Chrome, you can go to Developer tools (F12) -> Application -> Service worker and unregister the service workers from the list for that specific domain.
This method is effective in development mode of a site and mostly they run on localhost which is you may need for other project's development.
FYI, in case you are using MacOS Safari browser, there is one way to forcibly unregister a service worker (steps and images for Safari 12.1):
Safari > Preferences... > Privacy > Manage Website Data…
Enter domain name (ex. 'localhost'), click "Remove"
Note: In addition to service workers, this also will erase all caches, cookies, and databases for this domain.
In addition to the already correct answers given, if you want also to delete the SW cache you can invoke the following method:
if ('caches' in window) {
caches.keys()
.then(function(keyList) {
return Promise.all(keyList.map(function(key) {
return caches.delete(key);
}));
})
}
More in this article (Paragraph: "Unregister a service worker")
Another possibility, via Browser, is by accessing the "Cache Storage" section and click on the "Clear Site Data" button:
You should detecte two API in your devices: getRegistrations and getRegistration. The service-worker not has a unique set of APIs in all platforms. For example, some browsers only have a navigator.serviceWorker.getRegistration, no navigator.serviceWorker.getRegistrations. So you should consider with both.
safely uninstall Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (const registration of registrations) {
// unregister service worker
console.log('serviceWorker unregistered');
registration.unregister();
}
});
}
to detect service worker:
navigator.serviceWorker.controller
Code to for deletion of service worker:
navigator.serviceWorker.getRegistrations()
.then(registrations => {
registrations.forEach(registration => {
registration.unregister();
})
});
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for(let registration of registrations) {
registration.unregister()
} })
if(window.navigator && navigator.serviceWorker) {
navigator.serviceWorker.getRegistrations()
.then(function(registrations) {
for(let registration of registrations) {
registration.unregister();
}
});
}
if ('caches' in window) {
caches.keys()
.then(function(keyList) {
return Promise.all(keyList.map(function(key) {
return caches.delete(key);
}));
})
}
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (const registration of registrations) {
// unregister service worker
console.log('serviceWorker unregistered');
registration.unregister();
setTimeout(function(){
console.log('trying redirect do');
window.location.replace(window.location.href); // because without redirecting, first time on page load: still service worker will be available
}, 3000);
}
});
}
IF your service worker don't let you update your files. You will need to replace serviceworker file (sw.js / ServiceWorker.js) with the next code:
self.addEventListener('install', function(e) {
self.skipWaiting();
});
self.addEventListener('activate', function(e) {
self.registration.unregister()
.then(function() {
return self.clients.matchAll();
})
.then(function(clients) {
clients.forEach(client => client.navigate(client.url))
});
});
Source here
as for me , i just use a new nonexistent scope service worker to replace old one,
ServiceWorker: {
events: true,
// what range of URLs a service worker can control. Use a nonexistent path to disable ServiceWorker
scope: '/disable-service-worker/',
},
as for the app.js, i add below code to unregister old sw:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(registrations => {
for (const registration of registrations) {
// keep only serviceWorker which scope is /disable-service-worker/, The purpose is to make serviceWorker useless
if (registration.scope.includes('/disable-service-worker/') === false) {
registration.unregister()
}
}
});
// clear cache of service worker
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
return caches.delete(key);
}),
);
});
}
It can also be done in Chrome through application tab:
This code is compatible with Internet Explorer:
if (navigator.serviceWorker) {
navigator.serviceWorker.getRegistrations().then(
function(registrations) {
for (let idx in registrations) {
registrations[idx].unregister()
}
})
}
IE doesn't support 'for...of' and 'for...of' construction may lead to "'SCRIPT1004: Expected ';'" error
The other answers all add code to the live website to remove the service worker. However I didn't want to leave that live code running forever so I developed a solution that works from within the service worker itself. The steps are below, I posted more detail and explanation on my blog.
Delete the code that registers the service worker.
Replace the service worker script with the following file. The new code must be available from the same URL the previous service worker was at. If you had multiple service worker URLs in the past you should duplicate the code at all of them.
console.log("Cleanup Service Worker Starting");
caches.keys()
.then(keys =>
Promise.all(
keys.map(async key => console.log("caches.delete", key, await caches.delete(key)))))
.then(async () => {
console.log("registration.unregister", await registration.unregister());
})
.then(() => console.log("DONE"))
.catch(console.error);
This code is fairly straight forward. First it deletes all the caches, then it unregisters itself.
Users' browsers will automatically check for an updated service worker the next time they load your website, or the next event 24h after the last service worker check. This means that existing users will run this cleanup on their next visit.
If You want to unregister all of the registered service workers in Browser,
you can do it by opening ex.
Chrome: chrome://serviceworker-internals/
Brave brave://serviceworker-internals/
open DevTools > Console and paste this:
$$('.unregister').forEach(b => b.click())
Open this page: chrome://serviceworker-internals and click to unregister button.
If you want to unregister all service worker open the developer tools and run this code on above page.
document.querySelectorAll("button.unregister").forEach(item=>{ item.click()})
Open in Chrome
chrome://serviceworker-internals/?devtools
then F12 in Console
$$('.unregister').forEach(b => b.click())
Typical JavaScript loop thats compatible with everything:
navigator.serviceWorker.getRegistrations().then(function(registrations) {
var registrationslength = registrations.length;
for (var i = 0; i < registrationslength; i++) {
registrations[i].unregister();
}
})
I want to create a form on an index page that can store data via session storage. I also want to make sure that whatever data(let's say name) ... is remembered and used throughout the site with angular. I have researched pieces of this process but I do not understand how to write it or really even what it's called.
Any help in the right direction would be useful as I am in the infant stages of all of this angular business. Let me know.
The service you want is angular-local-storage.
Just configure it in your app.js file:
localStorageServiceProvider
.setStorageType('sessionStorage');
And then use it in the controller that contains whatever data you want to remember. Here is an example of a controller that loads the session storage data on initialization, and saves it when a user fires $scope.doSearch through the UI. This should give you a good place to start.
(function () {
angular.module("pstat")
.controller("homeCtrl", homeCtrl);
homeCtrl.$inject = ['$log', 'dataService', 'localStorageService', '$http'];
function homeCtrl ($log, dataService, localStorageService, $http) { {
if (localStorageService.get("query")) { //Returns null for missing 'query' cookie
//Or store the results directly if they aren't too large
//Do something with your saved query on page load, probably get data
//Example:
dataService.getData(query)
.success( function (data) {})
.error( function (err) {})
}
$scope.doSearch = function (query) {
vm.token = localStorageService.set("query", query);
//Then actually do your search
}
})
}()
On my meteor app I have a login system that sends you to the /dashboard path if you log in or sign up successfully. However, right now it is possible to get to the /dashboard path just by typing in localhost:3000/dashboard. How can I prevent this?
In addition to filtering the route with router hooks or custom actions, you may ensure that the template itself is displayed only to privileged users:
<template name="secret">
{{#if admin}}
...
{{/if}}
</template>
Handlebars.registerHelper('admin', function(options) {
if(Meteor.user() && Meteor.user().admin) return options.fn(this);
return options.inverse(this);
});
If you want to show a template to all registered users, you may use {{#if currentUser}} instead, in which case you don't need to register an additional helper.
You can accomplish this using before hooks. Here is a simple example with three routes: index, signin, and dashboard:
Router.map(function() {
this.route('index', {
path: '/'
});
this.route('signin');
this.route('dashboard');
});
var mustBeSignedIn = function() {
if (!(Meteor.user() || Meteor.loggingIn())) {
Router.go('signin');
this.stop();
}
};
Router.before(mustBeSignedIn, {except: ['signin']});
Before all routes except signin, we redirect the user back to the signin page unless they are logged in or in the process of logging in. You can see more examples in the using hooks section of the IR docs.
You need to check the state of the user before each route is run. If the user is not logged in (Meteor.userId() returns null) then redirect the user to the login route.
Router.before(function() {
if (!Meteor.userId()) {
this.redirect('userLoginRoute');
this.stop();
}
}, {
except: ['userLoginRoute', 'userSignupRoute', 'userNewPasswordRoute']
});
I believe you can use custom actions for iron-router. You can check Meteor.userId() if it's null (not logged in) in the custom action, and redirect accordingly.