Mais conteúdo relacionado Semelhante a Serverless Angular, Material, Firebase and Google Cloud applications (20) Serverless Angular, Material, Firebase and Google Cloud applications1. Serverless Angular + Material +
Firebase applications
Loiane Groner
@loiane
Build a full stack app with Google technologies
3. Agenda
Build a UI
Make it responsive
Build an API (Serverless)
Realtime database x Firestore
Authentication
Cloud Functions
Hosting
Deployment
15. Angular Material + Flex Layout
<mat-expansion-panel-header>
<mat-panel-title>
{{ module.name }}
"</mat-panel-title>
<mat-panel-description fxHide [fxHide.gt-md]=“false" >
{{ module.lessons.length}} Lessons"</mat-panel-description>
<span class="panel-title-time" fxShow [fxShow.xs]="false">{{ module.duration }}"</span>
"</mat-expansion-panel-header>
<mat-list dense *ngIf="module.lessons">
<ng-template ngFor let-lesson [ngForOf]="module.lessons">
<mat-list-item>
<mat-icon mat-list-avatar …>"</mat-icon>
<span mat-line>{{ lesson.name }}"</span>
<p mat-line fxHide [fxHide.xs]="false">{{ lesson.duration }} min"</p>
<div fxShow [fxShow.xs]="false" fxLayout="row">
<mat-icon fontSet="mdi" fontIcon=“mdi-clock" >"</mat-icon>{{ lesson.duration }}
"</div>
"</mat-list-item>
"</ng-template>
"</mat-list>
16. Angular Material + Flex Layout: Tablet
<mat-expansion-panel-header>
<mat-panel-title>
{{ module.name }}
"</mat-panel-title>
<mat-panel-description
fxHide [fxHide.gt-md]=“false" >
{{ module.lessons.length}} Lessons
"</mat-panel-description>
<span class="panel-title-time"
fxShow [fxShow.xs]=“false">
{{ module.duration }}
"</span>
"</mat-expansion-panel-header>
17. Angular Material + Flex Layout: Phone
<mat-expansion-panel-header>
<mat-panel-title>
{{ module.name }}
"</mat-panel-title>
<mat-panel-description
fxHide [fxHide.gt-md]=“false" >
{{ module.lessons.length}} Lessons
"</mat-panel-description>
<span class="panel-title-time"
fxShow [fxShow.xs]=“false">
{{ module.duration }}
"</span>
"</mat-expansion-panel-header>
19. Pros x Cons
No need to think about servers
No upfront provisioning; scales as needed
Pay per use
Stateless
Vendor Lock-in
37. Add Firebase config: dev and prod
export const environment = {
production: false,
firebase: {
apiKey: "AIzaSyDt4IqlLy7UYoPgCwwd_I6C7T5ZHjuFbrU",
authDomain: "devfest-bcc98.firebaseapp.com",
databaseURL: "https:"//devfest-bcc98.firebaseio.com",
projectId: "devfest-bcc98",
storageBucket: "devfest-bcc98.appspot.com",
messagingSenderId: "228593776604"
}
};
src/environment/environment.ts
38. Angular: AppModule
…
import { AngularFireModule } from '@angular/fire';
import { environment } from '"../environments/environment';
@NgModule({
imports: [
…,
AngularFireModule.initializeApp(environment.firebase)
],
bootstrap: [AppComponent]
})
export class AppModule { }
src/app/app.module.ts
39. Firebase modules
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireStorageModule } from '@angular/fire/storage';
@NgModule({
exports: [
AngularFireModule, "// needed for everything
AngularFirestoreModule, "// only needed for database features
AngularFireAuthModule, "// only needed for auth features
AngularFireStorageModule "// only needed for storage features
]
})
export class AppFirebaseModule { }
41. Firestore: retrieving collections / documents
export class FireApiService {
constructor(public db: AngularFirestore) {}
getCollection<T>(collectionName: string): Observable<T[]> {
return this.db.collection<T>(collectionName).valueChanges();
}
getCollectionItem<T>(collection: string, property: string, searchItem: string) {
return this.db
.collection<T>(collection, ref "=> ref.where(property, '"==', searchItem))
.valueChanges();
}
getDocument(path: string) {
"// path: /courses/XF40vxTSoSMNFXUndKaw
return this.db.doc(path).valueChanges();
}
}
42. Angular async pipe
@Component({
selector: 'app-courses-list',
template: `
<ul>
<li *ngFor="let course of courses$ | async">
{{ course.name }}
"</li>
"</ul>
`
})
export class CoursesListComponent {
courses$: Observable<any[]>;
constructor(db: AngularFirestore) {
this.courses$ = db.collection('courses').valueChanges();
}
}
43. Firestore: creating/updating data
export class FireApiService {
constructor(public db: AngularFirestore) {}
createDoc(path: string, obj: any): Observable<firebase.firestore.DocumentReference> {
return from(this.db.collection(path).add(obj));
}
updateDoc(path: string, obj: any) {
return this.db.doc(path).update(obj);
"// this.db.doc(path).set(obj); "// overwrite
}
deleteDoc(path: string) {
return this.db.doc(path).delete();
}
}
47. @angular/fire
export class AuthService {
constructor(private afAuth: AngularFireAuth) {}
get currentUser(): firebase.User {
return this.afAuth.auth.currentUser;
}
signInWithGoogleAuthProvider() {
return from(this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider()));
}
signInWithEmailAndPassword(
email: string, password: string
): Observable<firebase.auth.UserCredential> {
return from(this.afAuth.auth.signInWithEmailAndPassword(email, password));
}
signOut() {
return from(this.afAuth.auth.signOut());
}
}
48. Angular route guards
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | boolean {
if (!this.auth.currentUser) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
57. Http
import * as functions from 'firebase-functions';
"// "// Start writing Firebase Functions
"// "// https:"//firebase.google.com/docs/functions/typescript
"//
export const helloWorld = functions.https.onRequest((request, response) "=> {
response.send("Hello from Firebase!");
});
58. Firestore + auth trigger
export const createUser = functions.firestore
.document('users/{userId}')
.onCreate((snap, context) "=> {
"// Get an object representing the document
"// e.g. {'name': 'Loiane', 'course': Angular}
const newValue = snap.data();
const name = newValue.name;
"// perform desired operations ""...
});
export const sendWelcomeEmail = functions.auth.user().onCreate(user "=> {
"// ""...
});
66. Avoiding templates changes: pipes for the rescue
export class UserEnrolledPipe implements PipeTransform {
constructor(priva: UserService) {}
transform(user: any, course: Course): Observable<boolean> {
if (user "&& course) {
return this.userService.isUserEnrolled(course);
}
return of(false);
}
} <button
*ngIf="(user | userEnrolled:course | async); else: enroll"
[routerLink]="['/continue-course/', course.slug]"
>
Continue Course
"</button>
72. .travis.yml
language: node_js
node_js:
- node
before_script:
- npm install -g "--silent firebase-tools
script:
- npm run build
after_success:
- test $TRAVIS_BRANCH = "master" "&& test $TRAVIS_PULL_REQUEST = "false"
"&& firebase deploy "--only hosting
—token $FIREBASE_TOKEN "--non-interactive
notifications:
email:
on_failure: change
on_success: change