Search code examples
firebasepolymerfirebase-authenticationpolymer-1.0

Can't store user details in local-storage without using an observer (Firebase & Polymer authentication)


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>

Solution

  • 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.

    Closures Example

    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