Angular Firebase.utils.ts
Pequena caixa de ferraamentas que fui criando conforme trabalhava em projetos Angular com Firebase.
Atenção Eu uso a lib
@angular/fire
, quem ainda não conhece e quer experimentar, basta usar o schematicsng add @angular/fire
para configura-lo em seu projeto.
Motivo
- Você não precisa resolver mais de 1x o mesmo problema;
- Reduz a repetição de código;
- Elas ajudam bastante durante o desenvolvimento;
Então vamos a elas.
1. toDate
Quando você recupera dados do firebase, os campos de data vem do tipo Timestamp
, sendo necessário chamar o método toDate()
.
// to-date.util.ts
import { firestore } from 'firebase/app';
export const toDate = (value: firestore.Timestamp | Date): Date => {
if (!value) return;
return value instanceof firestore.Timestamp ? value.toDate() : value;
};
Casos de uso
1.1 mapToDate
Quando você precisa fazer o setValue
ou patchValue
do documento para o form, o campo de data não preenchera como o esperado, pois ele não reconhece o tipo Timestamp
, então criei outra função usando a toDate
.
// doc-to-date.util.ts
export function docToDate<T = any>(column: string | string[]) {
return (doc: T): T => {
Array.isArray(column)
? column.map(c => doc[c] = toDate(doc[c]))
: doc[column] = toDate(doc[column]);
return doc;
}
}
E também um operador rxjs
// map-to-date.operator.ts
export function mapToDate<T = any>(column: string | string[]) {
return (src: Observable<T>) =>
src.pipe(map(docToDate(column)));
}
Agora podemos usa-los de diversas maneiras:
export class PatientProfileComponent implements OnInit {
patient$: Observable<Patient>;
constructor(private afs: AngularFirestore) {}
ngOnInit(): void {
this.patient$ = this.afs.collection('pacientes')
.doc('pacienteX').valueChanges().pipe(
// Com a função
map(
docToDate<Patient>('birthday')
),
// Com o operador
mapToDate<Patient>(['birthday', 'created']),
// Então populamos o form
tap(data => this.form.patchValue(data))
);
}
}
1.2 toDatePipe
Podemos criar um pipe com o nome toDate, e usarmos direto na view.
// to-date.pipe.ts
import { DatePipe } from '@angular/common';
import { Pipe, PipeTransform } from '@angular/core';
import { toDate } from '@vibe/shared/util/formatting';
@Pipe({
name: 'toDate'
})
export class ToDatePipe implements PipeTransform {
constructor(readonly datePipe: DatePipe) { }
transform(value: any, arg: string = null): string {
return this.datePipe.transform(
toDate(value),
!!arg ? arg : 'mediumDate'
);
}s
}
Perceba que eu não deixo de usar o DatePipe
do angular, para conseguir injetalo no pipe você deve adiciona-lo como um provider no modulo onde declarou o pipe.
// seu.module.ts
import { NgModule } from '@angular/core';
import { DatePipe } from '@angular/common';
...
@NgModule({
providers: [
DatePipe
]
})
export class SeuModule { }
Assim conseguimos continuar formatando a data:
// seu.component.html
<p>
Criado em: {{ document.created | toDate:'h:mm:ss a' }}
</p>
<!-- Ou simplemente usar o padrão mediumDate definido -->
<p>
Criado em: {{ document.created | toDate }}
</p>
Lembra que na função toDate
eu faço a comparação verificando se é do tipo Timestamp
? Agora você pode usar o pipe em todas as situações, pois funciona com Timestamp
ou Date
e continuamos usando o DatePipe
do angular como provider.
c-ya
[]s