3

I'm building an Angular2 app on Firebase using angularfire2.

Items returned from the realtime db have additional fields on them (e.g. $key, $exists) that can be useful in your application. But if you don't include those keys in your model definitions, typescript throws errors.

For example, say I have a class called item:

export class Item {
  name: string,
  price: number,
  isOnSale: boolean
}

When that item is returned via angularfire2 it has additional firebase fields (e.g. $key, $exists, etc.), that I sometimes want to access:

constructor(private af: AngularFire){
  this.af.database.list('items').subscribe(items => {
    items.forEach(item => this.doSomethingWithDbItem(item));
  })
}

doSomethingWithDbItemAndThenUpdate(item: Item){
  // Now, if I want to access the $key field, I get a typescript error because
  // $key does not exist on my class definition

  if( item.name === 'toy_truck'){
    const key = item.$key; // Will throw error typescript compile error
    this.af.database.object(`items/${key}`).set({isOnSale: true})
  }
}

Is there a best practice for handling this? I could add the db keys directly to the model. Or maybe create a FB class that has $key, $exists, etc. and then have my Item class and other classes extend FB?

This is a bit of a contrived example, so code may not be exactly right, but hopefully my point/question is clear.

4
  • 1
    and what's wrong with specifying $key and $exists properties on the Item interface? Commented Jan 29, 2017 at 23:28
  • 1
    you could add them as optional properties of they aren't there all the time. :) Commented Jan 29, 2017 at 23:38
  • @toskv I could add the to the Item interface, but I have many models/db nodes and would need to add them to all, which seems redundant. So I was thinking of a base class. That would be my approach but i was wondering if there was a better way. Commented Jan 29, 2017 at 23:54
  • switching to interfaces is a better way of doing it. check out @cartant 's solution is the cleanest way in my opinion. :) Commented Jan 30, 2017 at 8:32

1 Answer 1

3

The items in this code:

this.af.database.list('items').subscribe(items => {
  items.forEach(item => this.doSomethingWithDbItem(item));
})

will be an array of Object instances. It will not be an array of Item instances. That is, item instanceof Item will be false.

So it doesn't make a lot of sense to tell TypeScript that that's what they are.

Instead, you should use interfaces to describe the shape of the models. And if you were to use interfaces, using a base interface with the AngularFire2 properties is trivial:

export interface AngularFireObject {
  $exists: () => boolean;
  $key: string;
  $value?: any;
}

export interface Item extends AngularFireObject {
  name: string;
  price: number;
  isOnSale: boolean;
}
Sign up to request clarification or add additional context in comments.

1 Comment

I see, I was using classes when what I should use is an interface. Thanks! @cartant

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.