// Assume we have the following data in the Database:
{
"name": {
"first": "Ada",
"last": "Lovelace"
}
}
// Test for the existence of certain keys within a DataSnapshot
var ref = firebase.database().ref("users/ada");
ref.once("value")
.then(function(snapshot) {
var a = snapshot.exists(); // true
var b = snapshot.child("name").exists(); // true
var c = snapshot.child("name/first").exists(); // true
var d = snapshot.child("name/middle").exists(); // false
});
Last active
June 7, 2021 12:03
-
-
Save jhahspu/667ded517b0ea672bfc6471d82fd77a3 to your computer and use it in GitHub Desktop.
Firebase
// SERVICE
getById(id: string) {
return this.db.collection(this.dbPath).doc(id);
}
create(product: Product): any {
return this.products$.add({...product});
}
updateProduct(product: Product, id: string):any {
return this.products$.doc(id).update(product);
}
// PRODUCT FORM -- CONSTRUCTOR
this.id = this.route.snapshot.paramMap.get('id');
if (this.id) {
// this.productService.getById(this.id).valueChanges().subscribe(data => {
// this.form.setValue(data);
// });
this.productService.getById(this.id).valueChanges().pipe(take(1)).subscribe(data => {
this.form.setValue(data);
})
}
this.form = this.formBuilder.group(
{
title: new FormControl('', Validators.required),
price: new FormControl('', Validators.compose([Validators.required, Validators.min(0)])),
category: new FormControl('', Validators.required),
imageUrl: new FormControl('', Validators.required)
}
);
// PRODUCT FORM
save(product) {
this.submitted = true;
if (this.form.invalid) {
return;
}
if (this.id) {
this.productService.updateProduct(product, this.id);
this.router.navigate(['admin/products']);
} else {
this.productService.create(product);
this.router.navigate(['admin/products']);
}
}
ng add @angular/fire
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireModule } from '@angular/fire';
import: [
...
AngularFireModule.initialize(environment.firebase),
AngularFirestoreModule,
...
]
const getObservable = (collection: AngularFirestoreCollection<Task>) => {
const subject = new BehaviorSubject([]);
collection.valueChanges({ idField: 'id' }).subscribe((val: Task[]) => {
subject.next(val);
});
return subject;
};
- the valueChanges method return an Observable that will emit new value every time the data changes
- also configure firestore to use 'id' as an idField
collectionA = getObservable(this.store.collection('collectionA'));
collectionB = getObservable(this.store.collection('collectionB'));
// OR
collectionB = this.store.collection('collectionB').valueChanges({idField: 'id'});
constructor(
// inject an instance of the AngularFirestore provider
private store: AngularFirestore
) {}
this.store.firestore.runTransaction(() => {
return Promise.all([
this.store.collection(collectionA).doc(id).delete(),
this.store.collection(collectionB).add(item) // item is an interface
]);
});
this.store.collection('[collection_name]').add(item);
this.store.collection(collectionA).doc(id).update(item);
this.store.collection(collectionA).doc(id).delete();
*ngIf="(collectionA | async)"
*ngFore="let c of collentionA | async"
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/combineLatest';
let hello = Observable.of('hello');
let world = Observable.of('world');
hello.combineLatest(world)
import 'rxjs/add/observable/combineLatest';
Observable.combineLatest(hello, world);
cats: FirebaseListObservable<any[]>
dogs: Array<any[]>;
subscription: Subscription;
ngOnInit() {
this.cats = this.db.list('/cats');
this.subscription = this.db.list('/dogs').subscribe(dogs => {
this.dogs = dogs;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
<div *ngFor="let cat of cats | async">{{cat.name}}</div>
<div *ngFor="let dog of dogs">{{dog.name}}</div>
catCount: Observable<number>;
dogName: Observable<string>;
ngOnInit() {
this.catCount = this.db.list('/cats')
.map(cats => {
return cats.length
})
this.dogName = this.db.object('/dogs/dRSsdfSDFSwS')
.map(dog => {
return dog.name
})
}
there are {{catCount | async}} cats in this list
the dog's name is {{dogName | async}}
human: FirebaseObjectObservable<any>;
dogs: Observable<any[]>;
ngOnInit() {
this.human = this.db.object('/humans/jeff')
this.dogs = this.human.switchMap(human => {
return this.db.list('/dogs', {
query: {
orderByChild: 'owner',
equalTo: human.name
}
})
})
}
<div *ngFor="let dog of dogs | async">{{dog.name}}</div>
dog: FirebaseObjectObservable<any>;
cat: FirebaseObjectObservable<any>;
animals: Observable<any[]>
ngOnInit() {
this.cat = this.db.object('/cats/asdddSHGHfghfghf');
this.dog = this.db.object('/dogs/TsdfsSHGHfghfghf');
this.animals = Observable.combineLatest(this.cat, this.dog);
}
<div *ngFor="let animal of animals | async">{{animal.name}}</div>
dog: FirebaseListObservable<any>;
currentDog = new BehaviorSubjet(null);
ngOnInit() {
this.dogs = this.db.list('/dogs');
}
changeDog(dog) {
this.currentDog.next(dog)
}
<div *ngFor="let dog of dogs | async" (click)="changeDog(dog)">{{dog.name}}</div>
<h2>Current Dog</h2>
<div>{{ (curentDog | async)?.name }}</div>
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow write, read: if isOwner(userId);
}
match /categories/{category} {
allow read: if true;
}
match /products/{products} {
allow read: if true;
allow write, create, update, delete: if isAdmin();
}
// Reusable function to determine document ownership
function isOwner(userId) {
return request.auth.uid == userId
}
// If User is Admin
function isAdmin() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
}
}
.read
- Describes if and when data is allowed to be read by users. .write
- Describes if and when data is allowed to be written. .validate
- Defines what a correctly formatted value will look like, whether it has child attributes, and the data type. .indexOn
- Specifies a child to index to support ordering and querying.
// No Security
{
βrulesβ: {
β.readβ: true,
β.writeβ: true
}
}
- data accessible only from Firestore console
// No Security
{
βrulesβ: {
β.readβ: false,
β.writeβ: false
}
}
// Only authenticated users can access/write data
{
βrulesβ: {
β.readβ: βauth != nullβ,
β.writeβ: βauth != nullβ
}
}
// Only authenticated users from a particular domain (example.com) can access/write data
{
βrulesβ: {
β.readβ: βauth.token.email.endsWith(β@example.comβ)β,
β.writeβ: βauth.token.email.endsWith(β@example.comβ)β
}
}
- gives each authenticated user a personal node at
/post/$user_id
where$user_id
is the ID of the user obtained through authentication
// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
// Validates user is moderator from different database location
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.writeβ: βroot.child(βusersβ).child(βmoderatorβ).val() === trueβ
}
}
}
}
// Validates string datatype and length range
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.validateβ: βnewData.isString()
&& newData.val().length > 0
&& newData.val().length <= 140β
}
}
}
}
// Checks presence of child attributes
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.validateβ: βnewData.hasChildren([βusernameβ, βtimestampβ])β
}
}
}
}
// Validates timestamp is not a future value
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
βtimestampβ: {
β.validateβ: βnewData.val() <= nowβ
}
}
}
}
}
// Prevents Delete or Update
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.writeβ: β!data.exists()β
}
}
}
}
// Prevents only Delete
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.writeβ: βnewData.exists()β
}
}
}
}
// Prevents only Update
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.writeβ: β!data.exists() || !newData.exists()β
}
}
}
}
// Prevents Create and Delete
{
βrulesβ: {
βpostsβ: {
β$uidβ: {
β.writeβ: βdata.exists() && newData.exists()β
}
}
}
}
ng new [app_name]
npm i --save firebase
ng add @angular/fire
npm i --save bootstrap
// Fire
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { environment } from 'src/environments/environment';
@NgModule({
declarations: [
...
],
imports: [
...
AngularFireModule.initializeApp(environment.firebaseConfig),
AngularFirestoreModule, // firestore
AngularFireAuthModule, // auth
AngularFireStorageModule, // storage
AngularFireDatabaseModule,
],
providers: [],
bootstrap: [AppComponent]
})
npm i -g firebase-tools
firebase --version
firebase login
firebase init
-> select "hosting"
-> select [app_name] from list
-> public = dist/[app_name]
{
"hosting": {
"public": "dist/organicshop",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow write, read: if isOwner(userId);
}
// Reusable function to determine document ownership
function isOwner(userId) {
return request.auth.uid == userId
}
}
}
ng build --prod
firebase deploy
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { User } from 'src/app/interfaces/user';
import firebase from 'firebase/app';
import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class AuthService {
user$: Observable<User>;
constructor(
private afAuth: AngularFireAuth,
private afs: AngularFirestore,
private router: Router
) {
this.user$ = this.afAuth.authState.pipe(
switchMap(user => {
if (user) {
return this.afs.doc<User>(`users/${user.uid}`).valueChanges();
} else {
return of(null);
}
})
)
}
async googleSignin() {
const provider = new firebase.auth.GoogleAuthProvider();
const credential = await this.afAuth.signInWithPopup(provider);
return this.updateUserData(credential.user);
}
private updateUserData({ uid, email, displayName, photoURL }: User) {
// sets user data to firestore on login
const userRef: AngularFirestoreDocument<User> = this.afs.doc(`users/${uid}`);
const data = {
uid,
email,
displayName,
photoURL
}
return userRef.set(data, { merge: true });
}
async signOut() {
await this.afAuth.signOut();
return this.router.navigate(['/']);
}
}
import { Component } from '@angular/core';
import { AuthService } from 'src/app/services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent {
constructor(public auth: AuthService) { }
}
<!-- template will replace this div -->
<div *ngIf="auth.user$ | async; then authenticated else guest">
</div>
<!-- User NOT logged in -->
<ng-template #guest>
<h3>Howdy, GUEST</h3>
<p>Login to get started...</p>
<button
(click)="auth.googleSignin()"
class="btn btn-primary">
Login with Google
</button>
</ng-template>
<!-- User logged in -->
<ng-template #authenticated>
<div *ngIf="auth.user$ | async as user">
<h3>Howdy, {{ user.displayName }}</h3>
<img [src]="user.photoURL">
<p>UID: {{ user.uid }}</p>
<button (click)="auth.signOut()">Logout</button>
</div>
</ng-template>
async function oneToMany() {
const { uid } = await auth.currentUser;
const ref = db.collection('accounts').doc(uid).collection('orders');
return ref.add({ someData });
}
// Group subcollections for unified query
db.collectionGroup('orders').orderBy('date').where('data', '==', [something])
async function manyToMany(){
const { uid, displayName } = await auth.currentUser;
const ref = db.collection('chats');
const members = {
[uid]: displayName
};
ref.set({members}, {merge: true});
}
const query = db.collection('chats').orderBy('members.[memeberid]')
// single read
query.get()
// realtime
query.onSnapshot(q=> q.docChanges().map(change => change.doc/newIndex/oldIndex/type))
db.enablePersistence({synchronizeTabs: true})
const ref = db.collection('foods').doc('broccoli')
ref.set({name: 'π₯¦'})
const query = ref.where('name', '==', 'π₯¦')
const start = 'The Fast and The Furious';
const end = start + '~';
movies
.orderBy('title')
.startAt(start)
.endAt(end)
query
.where('game', '==', 'completed')
.where('score', '==', 123)
.orderBy('time')
const ref = db.collection('recipes')
ref.add({
ingredients: [
'π¦',
'π₯',
'π'
]
});
ref.update({
ingredients: firebase.firestore.FieldValue.arrayUnion('π')
// or
ingredients: firebase.firestore.FieldValue.arrayRemove('π¦')
})
ref.where('ingredients', 'array-contains', 'π₯')
const ids = [
'a',
'b',
'c'
];
const readIds = (ids) => {
const reads = ids.map(id =>
db.collection('foo').doc(id).get()
);
return Promise.all(reads);
}
const { serverTimestamp, increment } = firebase.firestore.FieldValue;
ref.update({
timestamp: serverTimestamp(),
counter: increment(1)
})
const batch = db.batch();
batch.set(game, {score});
batch.set(user, {lifetimeScore});
batch.commit();
- use coldline buckets to save $ on storage
- storage location + download url
const storageRef = storage.refFromURL('gs://awesome-dev.appspot.com/giphy.gif');
const storageRef = storage.refFromURL('');
async function allFiles() {
const dir = storage.ref('images/sample123')
const allFiles = await storageRef.listAll();
}
const storageRef = storage.reg('image.png');
const task = storageRef.put(someBlob);
task.on(firebase.storage.TaskEvent.STATE_CHANGED, e => {
const progress = e.bytesTransferred / e.totalBytes;
})
const files = [...Array(20).keys()];
for (const f of files) {
storage.ref(f).put(someFile, {customMetadata: {userId, platform}})
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment