Search code examples
angularfirebasegoogle-cloud-firestoreangularfire2

How to create (set) data to Firestore using AngularFire?


I'm following the AngularFire Quickstart tutorial. I got Read working, which displays the data from my Firestore database in the Angular view. I'm stumped how to get Create (set) working.

My app.component.html displays a little text form field with a Submit button, and the displays the values in the Firestore database:

<form (ngSubmit)="onSubmit()">
  <input type="text" [(ngModel)]="name" name="name">
  <button type="submit" value="Submit" >Submit</button>
</form>

<ul>
  <li class="text" *ngFor="let item of items | async">
    {{item.name}}
  </li>
</ul>

My app.component.ts file has a function onSubmit() that executes when the user clicks the Submit button. It logs the name that the user entered in the text form field. The next line throws this error:

TS2339: Property 'firestore' does not exist on type 'AppComponent'.

23     this.firestore.collection('items').doc('item').set({name: this.name}); // error is here

Here's my app.component.ts:

import { Component } from '@angular/core';
import { AngularFirestore } from '@angular/fire/compat/firestore';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

  items: Observable<any[]>;
  constructor(firestore: AngularFirestore) {
    this.items = firestore.collection('items').valueChanges();
  }

  name: string | null = null;

  onSubmit() {
    console.log(this.name);
    this.firestore.collection('items').doc('item').set({name: this.name}); // error is here
    return // return what?
  }
}

This is the same pattern as the Tour of Heroes tutorial:

export class HeroService {

  constructor(private logger: Logger) {  }

  getHeroes() {
    this.logger.log('Getting heroes ...');
    return HEROES;
  }
}

My app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';

// Firebase
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

The view:

enter image description here

And my Firestore database:

enter image description here


Solution

  • I think the issue here is related to TypeScript. I believe best-practice, in this case, would be for you to use the private access modifier in front of firestore: Firestore while injecting it into the constructor of the component or service where you are using it, in your case app.component.ts.

    The private access modifier does a couple things:

    1. It makes the property a member of the class (similar to if you had declared the property in you class firestore: any;)
    2. It makes the property 'private', only available in the class but not available in the template (during development - not enforced in production).

    constructor(private firestore: AngularFirestore) {
      this.items = firestore.collection('items').valueChanges();
    }