@modelata/angular-fire
v8.0.1
Published
@modelata/fire implementation for angular 15
Downloads
211
Readme
modelata angular fire
modelata-angular-fire is a wrapper of @angular/fire with firestore for angular application modelata-angular-fire implement and extend modelata-fire
modelata-angular-fire gives abstract class and abstract dao to be extend.
Compatibility
| Angular | Firebase | AngularFire | @modelata/fire | @modelata/angular-fire | | --------|----------|--------------|----------------|------------------------| | 15 | 9.18 | ^7.5 | ^5.0.0 | ^8.0.0 | | 13 | 9.6 | ^7.2 | ^4.0.0 | ^7.2.0 |
Summary
Model
A model/class describes a type of document stored in Firestore.
Modelata adds the following features to it:
- simplified retrieval of a sub-collection content : @SubCollectionGetList
- simplified retrieval of a document referenced by a property of this class : @GetByRef
- simplified storage and retreival of a file referenced by a property of this class and stored in Firebase Storage: @StorageProperty
- "controlsConfig" generation of this class, to be used as parameter of myFormBuilder.group()
DAO service
Modelata provides the following "CRUD" functions to manipulate easily documents stored in Firestore:
- get list of documents in the collection
- get one document (by id or path or reference)
- create/save a new document (use getNewModel() to get a new blank instance of document)
- udpate an existing document
- delete (hard: remove it from Firestore / soft: add "deleted" flag)
Auth User
This Modelata service makes it easy to manage the current authenticated user:
- register and login by email & password
- is connected ?
- get data about him (his "FirebaseUser" and his Firestore own document)
- logout
----- MFModel -----
BASE
export class UserModel extends MFModel<UserModel> {
name?: string = null;
constructor(
data: Partial<UserModel>,
mustachePath: string,
location: Partial<IMFLocation>,
) {
super();
super.initialize(data, mustachePath, location);
}
}
- your models extends MFModel.
- this minimal constructor is same for all models. userModel is instanciate by UserDao only.
- all attributes must be set with a default value (any value).
PREFIX SUFFIX
its possible to add attributes in the model
attributes prefixed with _ will not be saved in database attributes that are observables must be suffixed by $
export class UserModel extends MFModel<UserModel> {
_notSavedInDb: number = null;
notSavedInDb$: Observable<any> = null;
_notSavedInDb$: Observable<any> = null;
savedInDb: number = null;
// ...
}
COMMON ATTRIBUTES
modelata-angular-fire set some attribute an all models.
- _id : document id.
- _collectionPath : document path.
- _snapshot : document firestore snapshot.
- _fromCache : true if the document comes from the cache.
- updateDate : date of last update.
- creationDate : creation date of document in db.
- deleted : boolean used for soft deletion mode
COMMON METHODS
- toString()
- toFormBuilderData()
- existsInDB()
- initialize()
DECORATORS
modelata-angular-fire give some decorator for your model attributes.
@SubCollectionGetList
@SubCollectionGetList<M = any>(collectionName: string, daoName: string, options?: IMFGetListOptions<M>)
Decorator to use on a model property. Its value will then be an observable of the list of documents present in the specified collection.
- @param collectionName name of the subCollection
- @param daoName dao used to fetch documents list
- @param options getListOptions (withSnapshot, completeOnFirst, where, orderBy, limit, offset, cacheable)
/!\ to use this Decorator, you must inject the dao as a dependency in your model's constructor. /!\
/!\ keep in mind that dependency injection is one way only and avoid injecting parents'dao in children models /!\
export class UserModel extends MFModel<UserModel> {
@SubCollectionGetList('subCollectionName', '_subUserDAOService')
_subUserCollectionDocs$: Observable<SubUserCollectionDocModel[]> = null;
@SubCollectionGetList('subCollectionName', 'subUserDAOService', {
orderBy: {
field: 'myDate',
operator: 'asc',
},
})
_subUserCollectionDocsSorted$: Observable<SubUserCollectionDocModel[]> = null;
constructor(
data: Partial<UserModel>,
mustachePath: string,
location: Partial<IMFLocation>,
protected _subUserDAOService: SubUserDAOService, // dao attribute name must be start with an underscore, else the DAO try to save it in database (cf prefix/suffix)
) {
super();
super.initialize(data, mustachePath, location);
}
}
@GetByRef
@GetByRef(attributeName: string, daoName: string)
Decorator to use on a model property. Its value will then be an observable of the document referenced by the linked attribute.
- @param attributeName The attribute refencing a document from database
- @param daoName The DAO used to fetch the document
/!\ to use this Decorator, you must inject the dao as a dependency in your model's constructor. /!\
export class UserModel extends MFModel<UserModel> {
@GetByRef('myRefDoc', '_myRefDAOService')
_myRef$: Observable<RefDocModel> = null;
myRefDoc: DocumentReference = null;
constructor(
data: Partial<UserModel>,
mustachePath: string,
location: Partial<IMFLocation>,
protected _myRefDAOService: MyRefDAOService, // dao attribute name must be start with an underscore, or else the DAO will try to save it in database (cf prefix/suffix)
) {
super();
super.initialize(data, mustachePath, location);
}
}
For GetByRef AND SubCollectionGetList. add MyRefDAOService via getNewModel method of UserDaoService
export class UserDaoService extends MFFlattableDao<UserModel> {
constructor(
db: AngularFirestore,
storage: AngularFireStorage,
protected myRefDAOService: MyRefDAOService, // Dependency injection
) {
super(db, storage);
}
getNewModel(
data?: Partial<UserModel>,
location?: Partial<IMFLocation>,
): UserModel {
const userModel = new UserModel(
data,
this.mustachePath,
location,
this.myRefDAOService,
); // add myRefDAOService for GetByRef AND SubCollectionGetList.
return userModel;
}
}
@InSubDoc
@InSubDoc(subDocPath: string)
Decorates a property that is constructed by DAO with the value of the same property of a subdocument.
- @param subDocPath the path of the subdocument (WITHOUT main document path)
/!\ the dao must be extend 'MFFlattableDao' (not MFDao).
export class UserModel extends MFModel<UserModel> {
@InSubDoc('protectedData/private')
phone: Observable<RefDocModel> = null;
// ....
}
@StorageProperty
@StorageProperty(options: IMFStorageOptions)
Decorates a property that is a file to save in firebase Storage. The property must be of type : IMFFile.
@StorageProperty({
deletePreviousOnUpdate: false,
deleteOnDelete: true
})
picture: IMFFile = null;
FORM
MFModel give two public methods toFormBuilderData
(and toString).
we have four decorators for add any validators on model attribute or add/remove attributes control.
toFormBuilderData
Returns data to build a form group
- @param an object {[modelAttributeName]:requiredBoolean} .
- @param value changes trigger ex:
'blur' // all control on blur
{
anwser:'blur'
name:'change'
} // each control with his value, other to default
{
default:'blur',
except:{
name:'change'
}
} // all control to blur except name on change
- @param an object {[modelAttributeName]:anyValue } - to give some data to ToFormGroupFunction.
// myComponent.component.ts
this.myFormGroup = this.angularFormBuilder.group(
myModel.toFormBuilderData({ phone: true }, 'blur', { anwser: sectionModel }),
);
@FormControlValidators decorator
@FormControlValidators(value?: ValidatorFn[])
Adds validators to form control when generating form group data
@FormControlValidators([Validators.minLength(2)])
public name: string = null;
@NotInFormControl decorator
@NotInFormControl()
Explicitly DOES NOT generates form control for this property
@ToFormControl decorator
@ToFormControl()
Generates form control for this property (ex: for a private attribute)
@ToFormGroupFunction decorator
@ToFormGroupFunction(fn: function(value,options: AbstractControlOptions,speacialData:any))
Generates specific form group data with the given function
@ToFormGroupFunction<AnswerModel>((defaultValue?: any, options: AbstractControlOptions = {}, sectionModel?: SectionModel) => {
if (!options.validators) {
options.validators = [];
}
if (sectionModel && sectionModel.textMaxLength) {
(options.validators as ValidatorFn[]).push(Validators.maxLength(sectionModel.textMaxLength));
}
if (sectionModel && sectionModel.textMinLength) {
(options.validators as ValidatorFn[]).push(Validators.minLength(sectionModel.textMinLength));
}
return [defaultValue, options];
})
answer: string = null;
----- DAO -----
BASE
@Injectable({
providedIn: 'root',
})
@CollectionPath('/users')
export class UserDaoService extends MFDao<UserModel> {
constructor(db: AngularFirestore, storage: AngularFireStorage) {
super(db, storage);
}
getNewModel(
data?: Partial<UserModel>,
location?: Partial<IMFLocation>,
): UserModel {
const userModel = new UserModel(data, this.mustachePath, location);
return userModel;
}
}
- your DAOs extends MFDao ( or MFFlattableDao if you use '@InSubDoc' decorator in model).
- this minimal constructor is same for all DAOs.
- this minimal getNewModel methode is same for all DAOs.
- DAOs are all decorated by @CollectionPath.
getNewModel
getNewModel methode is used by MFDao, MFFlattableDao and by your components/service for instanciate a new model. If you want to add data calculated from existing data (or any data that is not in database) to your models, this is the method to do it.
beforeSave
beforeSave methode is called by MFDao or MFFlattableDao on all model just before saving it to the database. If you want delete field or add calculated data, this is the method to do it.
beforeSave(dataReadyToSave, idOrLocation){
if (dataReadyToSave.birthdate){
dataReadyToSave.age = dataReadyToSave.birthdate.toAge();
delete dataReadyToSave.birthdate;
}
return dataReadyToSave;
}
@CollectionPath decorator
@CollectionPath('/users')
CollectionPath decorator must be used on all DAO. CollectionPath take in parameter a string representing the collection path in firestore db. If the collection is a subcollection (collection in a document), use the "mustache" syntaxe for all document id.
@CollectionPath('/users/{userId}/mySubCollection/{mySubDocId}/subSubcollection')
All methods that need an id or a location (like "get"), now take a Location with ids mentioned in CollectionPath.
const location = {
id: 'mySubSubDocId',
mySubDocId: 'id',
userId: 'id',
};
@DeletionMode decorator
@DeletionMode(MFDeleteMode.SOFT)
DeletionMode decorator is used for set the deletion strategy for this DAO. (default : HARD) DeletionMode take in parameter a enum value MFDeleteMode.SOFT or MFDeleteMode.HARD. MFDeleteMode.SOFT :
- when a dao delete a document (with delete methode), the document is just updated with delete = true;
- all getList calls have a "where filter" on deleted field MFDeleteMode.HARD :
- when a dao delete a document (with delete methode), the document is definitely deleted.
PUBLIC METHOD
get
get(idOrLocation: string | IMFLocation, options?: IMFGetOneOptions)
Get a model from database from id or location
- options :
export interface IMFGetOneOptions {
/**
* Document will include an hidden property containing document snapshot
*/
withSnapshot?: boolean;
/**
* Observable returned will complete on first result
*/
completeOnFirst?: boolean;
/**
* Request result will be cached in order to get a faster answer on same getOne request
*/
cacheable?: boolean;
/**
* Display an error in console when requested document not exists (default: true)
*/
warnOnMissing?: boolean;
}
getByReference
getByReference(reference: DocumentReference, options?: IMFGetOneOptions)
Get a model from database from its reference
getByPath
getByPath(path: string, options?: IMFGetOneOptions)
Get a model from database from its path
getList
getList(location?: MFOmit<IMFLocation, "id">, options?: IMFGetListOptions<M>)
Get a list of documents in the collection
- options :
export interface IMFGetListOptions<M> {
/**
* Documents will include an hidden property containing document snapshote
*/
withSnapshot?: boolean;
/**
* Observable returned will complete on first result
*/
completeOnFirst?: boolean;
/**
* Where conditions
*/
where?: IMFWhere[];
/**
* Order by
*/
orderBy?: IMFOrderBy;
/**
* Maximum result returned
*/
limit?: number;
/**
* boundary of the get, only one is applied
*/
offset?: IMFOffset<M>;
/**
* Request result will be cached in order to get a faster answer on same getList request
*/
cacheable?: boolean;
}
getModelFromSnapshot
getModelFromSnapshot(snapshot: DocumentSnapshot, options?: Partial<IMFGetOneOptions>)
get a model from a snapshot
getListByPath
getListByPath(path: string, options?: IMFGetListOptions<M>)
Get list of document by collection path
update
update(data: Partial<M>, location?: string | IMFLocation | M, options?: IMFUpdateOptions<M>)
update some field of a model.
- options :
/**
* List of file properties of the model M for which stored files MUST (true) or MUST NOT be deleted on document update
* (Overrides behaviour configured in model decorators)
*/
export type IMFDeletePreviousOnUpdateFilesOptions<M extends IMFModel<M>> = {
/**
* File property : true => the previous file will be deleted if updated
* File property : false => the fprevious ile will NOT be deleted if updated
*/ [fileAttribute in NonFunctionPropertyNames<M>]?: boolean;
};
/**
* Options to pass to update method
*/
export interface IMFUpdateOptions<M extends IMFModel<M>> {
deletePreviousOnUpdateFiles?: IMFDeletePreviousOnUpdateFilesOptions<M>;
}
create
create(data: M, location?: string | Partial<IMFLocation>, options?: IMFSaveOptions)
save a new model in db, update if already exist.
- options :
export interface IMFSaveOptions {
/**
* If document already exists, it will be fully overwritten
*/
overwrite?: boolean;
}
delete
delete(idLocationOrModel: string | IMFLocation | M, options?: IMFDeleteOptions<M>)
Delete a model by id
- options :
/**
* List of file properties of the model M for which stored files MUST (true) or MUST NOT be deleted on document deletion
* (Overrides behaviour configured in model decorators)
*/
export declare type IMFDeleteOnDeleteFilesOptions<M extends IMFModel<M>> = {
/**
* File property : true => the file will be deleted
* File property : false => the file will NOT be deleted
*/
[fileAttribute in NonFunctionPropertyNames<M>]?: boolean;
};
/**
* Options to pass to delete method
*/
export interface IMFDeleteOptions<M extends IMFModel<M>> {
deleteOnDeleteFiles?: IMFDeleteOnDeleteFilesOptions<M>;
cascadeOnDelete?: boolean;
mode?: MFDeleteMode; // used for override defaultvalue (HARD or @DeletionMode)
}
deleteByReference
deleteByReference(reference: AngularFirestoreDocument<M>)
Delete a model by its reference
getReference
getReference(idOrLocationOrModel: string | Partial<IMFLocation> | M)
Get a reference from an id, a location or directly from model
getReferenceFromPath
getReferenceFromPath(path: string)
Get a reference from a compatible path
getSnapshot
getSnapshot(idOrLocation: string | IMFLocation, options?: IMFGetOneOptions)
Get a document snapshot from database from an id or a location
isCompatible
isCompatible(doc: M | DocumentReference | CollectionReference)
Check if the model or reference is compatible with this DAO based on its path
----- CACHE -----
with modelata-angular-fire, all database request result are cached with a bypass ( like behaviorSubject )
all current cached results are automatically destroyed on auth user logout.
setClearAllCacheObservable(clearAllCacheAndSubscription$: Observable)
MFCache.setClearAllCacheObservable(clearAllCacheAndSubscription$: Observable<any>)
DECORATOR
DisableCache
dao class decorator Tells the DAO to NOT cache the result
@DisableCache // without '()'
export class UserDaoService extends MFDao<UserModel>
Cacheable
method decorator Tells the Dao to cache request results
export class UserDaoService extends MFDao<UserModel> {
// ...
@Cacheable
public getFooByBar(): Observable<Foo> {
// return myObservable
}
}