I created a simple login with Polymer & Firebase (google authentication). I came across a problem which got me thinking that my chosen approach might not be the cleanest. So this question is more about the general pattern.
In my code below I combined the basic login from the latest Polycast video and some code I found in the Firebase Guide.
The Problem
Basically I am unable to call a function inside the login that would then store the values, neither can I store the returned user value's inside the login method directly.
My solution
To work around that problem I created a listener which fires if a user exists and then calls the modification method to store the user details inside my localstorgae userDetails object.
So I am wondering if my approach is okey and what I could improve to skip the observer and store the user details inside the login method. In addition I found in the Firebase guidelines a special observer but was not sure how to implement it.
<dom-module id="my-app">
<template>
<style>
:host {
display: block;
}
</style>
<firebase-app auth-domain="...firebaseapp.com"
database-url="...firebaseio.com"
api-key="..."></firebase-app>
<iron-localstorage id="localstorage" name="my-app-storage"
value="{{userDetails}}"></iron-localstorage>
<section class="wrapper">
<paper-material id="loginContainer" elevation="1">
<firebase-auth id="auth"
user="{{user}}"
status-known="{{statusKnown}}"
provider="{{provider}}"
on-error="handleError"></firebase-auth>
<template is="dom-if" if="[[user]]">
<h1>Welcome [[user.displayName]]</h1>
</template>
<paper-button raised on-tap="login" hidden$="[[user]]">Sign in</paper-button>
<paper-button raised on-tap="logout" hidden$="[[!user]]">Sign out</paper-button>
</paper-material>
</section>
</template>
<script>
Polymer({
is: 'my-app',
properties: {
userDetails: {
type: Object,
},
user: {
type: Object,
observer: '_changed'
},
statusKnown: {
type: Object
},
provider: {
type: String,
value: 'google'
}
},
login: function() {
this.$.auth.signInWithPopup(this.provider).then(function(result) {
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
//UNABLE TO CALL A METHOD OR TO STORE VALUES DIRECTLY IN MY OBJECT
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
var email = error.email;
var credential = error.credential;
});
},
//OBSERVER THAT CALLS THE MODIFICATION FOR THE LOCALSTORAGE
_changed: function() {
if (this.user != null) {
var user = firebase.auth().currentUser;
this.makeModifications(user);
}
},
// set localstorage
makeModifications: function(user) {
this.set('userDetails.name', user.displayName);
this.set('userDetails.email', user.email);
this.set('userDetails.photoUrl', user.photoURL);
console.log(this.userDetails.name + ' is singed in');
},
logout: function() {
this.$.localstorage.reload();
this.set('userDetails.name', null);
this.set('userDetails.email', null);
this.set('userDetails.photoUrl', null);
return this.$.auth.signOut();
},
});
</script>
</dom-module>
You have a problem with scope?. When inside this.$.auth.signInWithPopup().then(function you cant use Polymers this.set because your within a new scope. Theres also nothing wrong with what you've done, asuming its works for you.
this.$.auth.signInWithPopup().then(function(user) {
// notice bind(this), binds your polymer element to the scope of the
// function, this allows you to use its API, thus enabling
// the use of set within the Promise
this.set('userDetails.name', user.displayName);
}.bind(this));
this can also be done via
login: function() {
// create a local reference to the Polymer element
// assuming its within a closure like this login function it will be
// available to any function called from it.
var that = this;
this.$.auth.signInWithPopup().then(function(result) {
var user = result.user
that.set('userDetails.name', user.displayName);
});
}
I havent tested this code while writing so copy with caution.
The way I've been doing it recently is
login: function() {
this.$.auth.signInWithPopup()
.then(this._setUserDetails.bind(this))
.then(this._anotherFunctionCall.bind(this));
},
_setUserDetails: function(user) {
this.set('userDetails.name', user.displayName);
return Promise.resolve();
}
Its not doing anything extra just feels cleaner