@modelata/angular-fire
TypeScript icon, indicating that this package has built-in type declarations

8.0.1 • Public • Published

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.

consult doc

get the demo app

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
  }
}

----- AUTHENTICATION -----

BASE

FUNCTIONS

DECORATOR

----- LOGS -----

Versions

Current Tags

Version History

Package Sidebar

Install

npm i @modelata/angular-fire

Weekly Downloads

0

Version

8.0.1

License

ISC

Unpacked Size

141 kB

Total Files

38

Last publish

Collaborators

  • moventes