Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syncing Typescript types to Firebase Object Observables without losing functions etc

I am working on an Angular 2 app with Typescript and I'm constantly having issues with the fact that when I cannot seem to persist object types very easily when syncing data.

I have been manually setting properties. In some cases I'm just removing the $exists(), $key, createdat functions and properties to be able to update the data.

Is there a way to set a class from a Firebase object without completely changing it?

For an example:

search$:FirebaseObjectObservable<ISearchQuery>;
search:ISearchQuery;

constructor(public af: AngularFire, public auth: AuthService){
  this.search$ = this.af.database.object(`/queries/saved/${auth.id}`);
  //THIS IS HOW I WANT TO DO IT
  this.search$.subscribe((data)=>{
    if(data.$exists()){
       this.search=data;           
    }
  });  

  //THIS IS WHAT I'VE RESORTED TO
  this.search$.subscribe((data)=>{
    if(data.$exists()){
      this.search.categories = data.categories;
      this.search.creators = data.creators;
      this.search.endDate = data.endDate;
      this.search.startDate = data.startDate;
      this.search.location = data.location;
     }
  });  
}

On the flip side when I've synced and updated data then I have to pick each property when updating or setting to firebase. I also run into issues if I sync directly I lose class functions as well (since firebase objects have their own set of functions in the prototype).

Is there a way to avoid picking property by property or a better way of dealing with syncing Firebase objects with Typescript?

like image 456
Shane Loveland Avatar asked Sep 12 '25 12:09

Shane Loveland


1 Answers

It sounds like you want AngularFire2 to preserve the Firebase snapshots. By default, it 'unwraps' the snapshots, adding the $-prefixed property and function that you do not want.

If you specify the preserveSnapshot option, it won't do this and you can call snapshot.val():

constructor(public af: AngularFire, public auth: AuthService) {

  this.search$ = this.af.database.object(`/queries/saved/${auth.id}`, {
    preserveSnapshot: true
  });
  this.search$.subscribe((snapshot) => {
    if(snapshot.exists()) {
       this.search = snapshot.val();
    }
  });
}

And if you are going to need it later, you can always keep the snapshot.

Regarding ISearchQuery - which I am assuming is an interface - the value you receive from snapshot.val() is an anonymous object. It's safe to cast it to an interface that describes the 'shape' of the data, but if that interface includes methods, it is not going to work because the methods will not exist on the anonymous object. If you have a class that implements that interface, you should include a constructor that accepts the anonymous object you receive from snapshot.val().

For example:

this.search$.subscribe((snapshot) => {
  if(snapshot.exists()) {
    // Given SomeSearchQuery implements ISearchQuery
     this.search = new SomeSearchQuery(snapshot.val());
  }
});
like image 94
cartant Avatar answered Sep 14 '25 03:09

cartant