4

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?

1 Answer 1

2

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());
  }
});
Sign up to request clarification or add additional context in comments.

3 Comments

That is great, and it looks like it's half of the answer as far as re-syncing the data without having to strip out functions etc... However, It looks like I still lose the functions on my ISearchQuery type when I've instantiated a class this way that I want to preserve. Is there a way to do that?
Updated the answer.
That saves me a lot of headache. I was able to sync the object directly without removing the $exists, $key etc... That makes sense on using the constructor to pass through the anonymous object. Thanks a million!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.