2016年12月29日 星期四

[ASP.NET Core X Angular] (10) - Firebase integration

 Angular     MVVC    ASP.NET Core    Firebase    angularfire2  


Introduction


We will integrate Firebase database service in this article with angularfire2 package.

Related articles


Environment

Visual Studio 2015 Update 3
NPM: 3.10.3                                    
Microsoft.AspNetCore.Mvc 1.0.0
angular 2: 2.1.0
angularfire2 : 2.0.0-beta.6



Firebase


Firebase* is a NoSql cloud databse, create a new project and enable read and write permission before we start implementing authorization.

*For more information, go to https://firebase.google.com/docs/database/

Update the rules like this, see more database rules here.

{
  "rules": {
    ".read": true,
    ".write": true
  }
}




Install angularfire2

Use the following command to install angularfire2 and firebase*

$>npm install angularfire2 –save
$>npm install firebase –save

PS. Since angularfire2 is in beta, it doesn’t support “Storage” api so far. However, we can still use firebase official js library to develop the functions.
 
Optional: If you develop with Angular CLI and want to deploy the website to firebase, in stall firebase-tools as well.

Then you should update systemjs.config.js to include the packages.











Integrate Firebase database


We will update the sample codes in [ASP.NET X Angular2](7) – Sub-routing, which contains the constant products’ data in memory not in database.


Our goal is making the product.service support CRUD functions for data in Firebase.


Firebase configuration

Create a FirebaseConfig module to store the api key and other necessary information for connecting to firebase.

PS. You can find your api key in Firebase by clicking the icon below.



FirebaseConfig.ts

export module FirebaseConfig {
    var config: any;
    export function Get() {
        config = {
            apiKey: "XXXXXXXXXXXX",
            authDomain: "xxxx.firebaseapp.com",
            databaseURL: "https://xxxx.firebaseio.com",
            storageBucket: "xxxxx.appspot.com",
            messagingSenderId: "XXXXXXXXXXX"
        };

        return config;
    }
}


product.app.module.ts

Inject the angularfire2 package and api key config in to product.app.module.ts

//...
import { AngularFireModule } from 'angularfire2';
import {FirebaseConfig} from '../../class/FirebaseConfig';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        ProductRoutes,
        AngularFireModule.initializeApp(FirebaseConfig.Get())
    ],
    declarations: [
        //...
    ],
    bootstrap: [ProductAppComponent]
})
export class ProductAppModule { }




Update product.service with Firebase integration

Now we are going to update product.service to get the data from Firebase.

Tips:
Notice that everything in Firebase is a URL, so if the database structure is like this,




Get the entire products:
constructor(private af: AngularFire) {}
this.af.database.object('/Demo/products');


Get the first object with key:
this.af.database.object('/Demo/products/0');


Okay, let’s update our Service to integrate Firebase real-time data.

product.service.ts

//...
import { AngularFire, FirebaseListObservable  } from 'angularfire2';

@Injectable()
export class ProductService {
    constructor(private af: AngularFire) {}

    //Create
    public create(prod:Product){
        return new Promise(
            resolve => {
                let itemObservable = this.af.database.object('/Demo/products/' + prod.Id);
                itemObservable.set(prod); //Set will overwrite
                resolve();
            });
    }

    //Update
    public update(prod: Product) {
        return new Promise<Product>(
            resolve => {
            let itemObservable = this.af.database.object('/Demo/products/' + prod.Id);
            itemObservable.update(prod); //Update current data
            resolve();
        })

    };

    //Remove
    public remove(prod: Product) {
        return new Promise(
            resolve => {
            let itemObservable = this.af.database.object('/Demo/products/' + prod.Id);
            itemObservable.remove(); //Remove current data
            resolve();
        })
    };


    //Get data with key
    public get(key: string) {
        return new Promise<Product>(
            resolve => {
            this.af.database.object('/Demo/products/' + key).subscribe(data => {
                resolve(data);
            })
        });
    }
   

    //Get books (or toys/music)
    public getBooks() {
        return new Promise<Product[]>(
            resolve => {

            this._queryProducts().subscribe(data => {
                if (data) {
                    let books = data.filter(x => x.Type == "Book");
                    resolve(books);
                }
                else {
                    resolve([]);
                }

            })

        });
    }
   
    //Query data from firebase
    private _queryProducts() {
        return this.af.database.object('/Demo/products');
    }
}

Notice that we could use “retrieving-data-as-objects” way in the above codes, or either use “retrieving-data-as-lists” way to complete the same functions.



Demo





Integrate Firebase authentication


Remember that in the beginning we set both “read” and “write” are PUBLIC with the database.

Now try set the rules as “must-be-authorized”,

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

And you will get the error message like this…
EXCEPTION: permission_denied at /Demo/products: Client doesn't have permission to access the desired data.

So in the next step, we have to create a login and authorization page for Firebase and make sure the authorized user could have the permission to read/write the database.


Enable OAuth provider

We will use Google OAuth for example, make sure the Google Authentication provider  is enabled in your Firebase console.




Enable OAuth provider

We will put the login/logout functions on the below app.component.
If the user already logins, show the user’s information and logout button on the page.



app.component.html

<div class="card">
    <div class="card-block">
        <div class="card-text" [ngSwitch]="isAuth">
            <div *ngSwitchCase="false" class="text-center">
                <button class="btn btn-toolbar" (click)="login('google')">
                                <img width="30" src="../asset/images/logo/google-logo.png" />
                                Use Google Account
                            </button>
            </div>
            <div *ngSwitchCase="true" class="text-center">
                <table class="table">
                    <tr>
                        <td class="text-center">
                            <label class="control-label">{{user.name}}</label>
                        </td>
                    </tr>
                    <tr>
                        <td class="text-center">
                            <label class="control-label">{{user.email || '(no email)'}}</label>
                        </td>
                    </tr>
                </table>
                <div>
                    <input type="button" class="btn btn-warning" (click)="logout()" value="Logout" />
                </div>
            </div>
        </div>

    </div>
</div>



The HTML should be displayed like this.










app.component.ts

Here are the login/logout functions, notice that we check the user’s login state in constructor.


/// <reference path="../lib-npm/typings/jsnlog.d.ts" />
import { Component, OnInit } from '@angular/core';
import { AngularFire, AuthProviders, AuthMethods } from 'angularfire2';


@Component({
    selector: 'core-app',
    templateUrl:'/app/app.component.html'
})
export class AppComponent implements OnInit {

    private isAuth = false;
    private user = {};


    constructor(private af: AngularFire) {

        //Check the login state
        this.af.auth.subscribe(
            user => this._changeState(user),
            error => JL("Angular2").error(error)
        );
    }

    ngOnInit() {

    }

    //Login
    private login(provider: string) {
        this.af.auth.login({
            provider: this._getProvider(provider),
            method: AuthMethods.Popup
        });
    }

    //Logout
    private logout() {
        this.af.auth.logout();
    }

    //Check if the user is login or not
    private _changeState(user: any = null) {
        if (user) {
            this.isAuth = true;
            this.user = this._getUserInfo(user)
        }
        else {
            this.isAuth = false;
            this.user = {};
        }
    }

    //Get the user information from Provider's user data
    private _getUserInfo(user: any): any {
        if (!user) {
            return {};
        }
        let data = user.auth.providerData[0];
        return {
            name: data.displayName,
            avatar: data.photoURL,
            email: data.email,
            provider: data.providerId
        };
    }

    private _getProvider(provider: string) {
        switch (provider) {
            case 'twitter': return AuthProviders.Twitter;
            case 'facebook': return AuthProviders.Facebook;
            case 'github': return AuthProviders.Github;
            case 'google': return AuthProviders.Google;
        }
    }


app.module.ts

Of course we have to import the necessary AngularFireModule, AuthProviders, AuthMethods and also initialize AngularFireModule in app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { AngularFireModule, AuthProviders, AuthMethods } from 'angularfire2';
import {FirebaseConfig} from './class/FirebaseConfig';

@NgModule({
    imports: [
        BrowserModule,
        AngularFireModule.initializeApp(FirebaseConfig.Get())
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }





Demo






Github






Reference




沒有留言:

張貼留言