ngxs-labs/firestore-plugin

Mapping custom objects to firebase

jaytonic opened this issue ยท 6 comments

Hi,

In my app, I've a collection of object that look like this:

export class Trip {
  id: string;
  owner: string;
  name: string;
  startDate: Timestamp;
  endDate: Timestamp;
  coverImageUrl: string;
  itinerary: Itinerary;

  constructor() {}
}

export class Itinerary {
  stats: ItineraryStats;
  steps: Step[];

  constructor() {
    this.steps = Step[0];
  }
}

export abstract class Step {
  start: Date;
  end: Date;
  duration: number;
  enforcedStartStop: boolean;
  warning: string;
}

//.. then I got concrete objects

When I try to update one trip from my list, I get:

ERROR FirebaseError: Function DocumentReference.set() called with invalid data (via `toFirestore()`). Unsupported field value: a custom object (found in field itinerary in document trips/KBbdsG6GYVCGUYLqKAfe)
    at new n (http://localhost:8100/vendor.js:39963:23)
    at Gu (http://localhost:8100/vendor.js:53855:16)
    at t.i_ (http://localhost:8100/vendor.js:53575:16)
    at Mu (http://localhost:8100/vendor.js:53808:37)
    at Uu (http://localhost:8100/vendor.js:53692:50)
    at http://localhost:8100/vendor.js:53792:17
    at _ (http://localhost:8100/vendor.js:40153:68)
    at Cu (http://localhost:8100/vendor.js:53791:56)
    at Ru (http://localhost:8100/vendor.js:53616:19)
    at n.set (http://localhost:8100/vendor.js:55193:40)

Is there a way to map my objects to POJO and then POJO to my objects when using your firestore plugin? What would you recommend me?

hi @jgrossrieder

you can check firebase.firestore.FirestoreDataConverter

export class RacesFirestore extends NgxsFirestore<Race> {
  protected path = 'races';
  idField = 'raceId';

  converter: firebase.firestore.FirestoreDataConverter<Race> = {
    toFirestore: (value) => {
      const db = { ...value };
      delete db.testProp;
      return db;
    },
    fromFirestore: (snapshot, options) => {
      const data = snapshot.data(options);

      return <Race>{ ...data, testProp: data.id + data.title };
    }
  };
}

more information here

it's perfect, thank you!

@joaqcid I finally ended by using this to convert my objects to plain json and back:

https://github.com/typestack/class-transformer
It works nicely, I just have a weird issue with dates(if i only convert back and forth using class-transformer, it works, but if it goes to firebase and come back I get an error, like if firebase detects its a date and change its type :'(

Any dates coming back from firestore will be the firebase.firestore.Timestamp type. It would be worth having a bit of a look into this in the firestore docs on the how and way of this.
I have a few utility methods that I made for converting these dates:

export function isTimestampEqual(
  a: Date | firebase.firestore.Timestamp,
  b: Date | firebase.firestore.Timestamp
) {
  return isEqual(asDate(a), asDate(b));
}

export function asDate(value: any): Date {
  if (value && value.toDate) {
    return value.toDate();
  }
  return new Date(value || 0);
}

export function asDateOrNull(value: any): Date | null {
  if (!value) {
    return value;
  }
  return asDate(value);
}

export function toFSTimeStamp(date: Date) {
  return firebase.firestore.Timestamp.fromDate(date);
}

Hope this helps ๐Ÿ‘

@joaqcid Yes I was doing this before, but since now, using class-transformer, I'm only providing a text-format of the date, I was expecting to have a text-format back.

The thing is that class-transformer has a descriptive approach and I was hopping to be able to generate a full "JSON" object out with it that I could have reused at some other places(export/import). I will check if I can create a @FirebaseDate() descriptor, thanks for the good points.