3

I'm using Cloud Firestore to cast to an object which is working fine. However it seems I'm unable to call methods on that object. Here's my model definition -

contact.ts

export class Contact {
  id: string
  firstname: string
  lastname: string
  email: string

  getFullname() {
    return this.firstname + this.lastname
  }
}

contact.service.ts

@Injectable()
export class ContactService {
  getAll(raiseId: string): Observable<Contact[]> {
    this.contactsCollection = this.afs.collection<IContact>('contacts')
    this.contacts$ = this.contactsCollection.snapshotChanges().pipe(
      map(actions => actions.map(a => {
        const contact = a.payload.doc.data() as Contact;
        const id = a.payload.doc.id;
        return { id, ...contact };
      }))
    );
    return this.contacts$;
  }
}

contact.component.ts

@Component({
  selector: 'contact-view',
  templateUrl: './contact-view.component.html',
  styleUrls: ['./contact-view.component.scss']
})
export class ContactViewComponent implements OnInit {
  contacts$: Observable<Contact[]>;
  contacts: Contact[];

  constructor(
    public contactService: ContactService
  ) { }

  ngOnInit() {
      this.contacts$ = this.contactService.getAll();
      this.contacts$.subscribe(contacts => {
        this.contacts = contacts;
      })
    })
  }
}

component.component.html

<div *ngFor="let contact in contacts">{{contact.getFullname()}}</div>

But the getFullname() method just throws an error

TypeError: _v.context.$implicit.getFullname is not a function

Could someone explain why this is an if there is a way to call a function on a cast object?

Chris Edgington
  • 2,937
  • 5
  • 23
  • 42

1 Answers1

6

You can't simply cast the object returned by Firestore to any object and assume that it will just work. The object you get from calling data() is just a plain old JavaScript object with properties that match the fields of the document. That's all. It doesn't have any methods, and casting that object to something else doesn't create any methods for you. Casting just changes TypeScript's notion of what that object is, and in this case, you've just fooled TypeScript into thinking you have an instance of Contact, when you really don't.

If you want to turn that Firestore data object into a Contact object, you're going to have to copy the properties of the data object into a new Contact object. An easy way to do that is using Object.assign().

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Thanks Doug. My complete lack of understanding here. Appreciate your help. – Chris Edgington Dec 02 '18 at 12:37
  • Knowing what was wrong I ended up here looking for a good solution. `Object.assign()` seems to be the best practice solution for this. For similar use cases some prefer object spread (see https://stackoverflow.com/questions/32925460/object-spread-vs-object-assign) – N4ppeL Dec 06 '19 at 10:38