angularfirebaseionic-framework

NullInjectorError: No provider for Firestore


ERROR NullInjectorError: R3InjectorError(Standalone[ReservarPage])[ReservasService -> ReservasService -> Firestore -> Firestore]: NullInjectorError: No provider for Firestore!

I dont know how to fix this i have been trying a lot of things to fix this but I cant, and this is my code

    import { Component } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { FormControl, FormGroup, FormsModule, Validators, ReactiveFormsModule } from '@angular/forms';
    import { IonicModule } from '@ionic/angular';
    import { RouterLink } from '@angular/router';
    import { MenuController } from '@ionic/angular';
    import { ReservasService } from '../reservas.service';

    @Component({
      selector: 'app-reservar',
      templateUrl: './reservar.page.html',
      styleUrls: ['./reservar.page.scss'],
      standalone: true,
      imports: [CommonModule, FormsModule, RouterLink, IonicModule, ReactiveFormsModule ]
    })
    export class ReservarPage  {

      reservaForm = new FormGroup({
        fechaInicio: new FormControl("", [Validators.required]),
        lugares: new FormControl("", [Validators.required]),
        puertoSalida: new FormControl("", [Validators.required]),
        noches: new FormControl("", Validators.required),
        huespedes: new FormControl("", Validators.required),
        nombreCamarote: new FormControl("", Validators.required),
        numeroCamarotes: new FormControl("", Validators.required),
        nombreCrucero: new FormControl("", Validators.required)
      })

      async onSubmit(){
        const datos = this.reservaForm.value
        await this.reservasService.guardarReserva(
          this.reservaForm.value.fechaInicio, 
          this.reservaForm.value.lugares, 
          this.reservaForm.value.puertoSalida,
          this.reservaForm.value.noches, 
          this.reservaForm.value.huespedes, 
          this.reservaForm.value.nombreCamarote, 
          this.reservaForm.value.numeroCamarotes, 
          this.reservaForm.value.nombreCrucero)
      }
  
      constructor(public menu: MenuController, public reservasService: ReservasService) { 
        this.menuActive();
      }

      menuActive(){
        this.menu.enable(true, 'menu');
      }

    }
    ```

    resrvas.service.ts:
    ```
    import { inject, Injectable } from "@angular/core";
    import { addDoc, collection, Firestore } from "@angular/fire/firestore";

    @Injectable({
        providedIn: 'root',
    })
    export class ReservasService {
        private firestore = inject(Firestore);
        constructor( ) {  }

        async guardarReserva(fechaInicio: any, lugares: any, puertoSalida: any, noches: any,
                             huespedes: any, nombreCamarote: any, numeroCamarotes: any, 
                             nombreCrucero: any) {
            const obj ={
                "fechaInicio" : fechaInicio,
                "lugares" : lugares,
                "puertoSalida" : puertoSalida,
                "noches" : noches,
                "huespedes" : huespedes,
                "nombreCamarote" : nombreCamarote,
                "numeroCamarotes" : numeroCamarotes,
                "nombreCrucero" : nombreCrucero
            };

            const Ref = collection(this.firestore, 'Reservas');

            await addDoc(Ref, obj);
        }
    }
    ```

    authentication.service.ts:
    ```
    import { inject, Injectable } from '@angular/core';
    import { Auth } from '@angular/fire/auth';
    import { createUserWithEmailAndPassword, sendPasswordResetEmail, signInWithEmailAndPassword, signOut } 
    from 'firebase/auth';
    import { User } from 'firebase/auth';

    @Injectable({
      providedIn: 'root',
    })
    export class AuthenticationService {
      private auth = inject(Auth);
      constructor(
      ) { }

      async registerUser(email: string, password:string){
        return await createUserWithEmailAndPassword(this.auth, email, password)
      }

      async loginUser(email: string, password: string){
       return await signInWithEmailAndPassword(this.auth, email,password)
      }

      async resetPassword(email: string){
        return await sendPasswordResetEmail(this.auth, email)
      }

      async signOut(){
        return await signOut(this.auth)
      }

      async getProfile():Promise <User | null> {
        return new Promise<User | null>((resolve, reject) => {
          this.auth.onAuthStateChanged(user => {
            if (user) {
              resolve(user as User);
            } else {
              resolve(null);
            }
          }, reject);
        })
      }
    }
    ```


    app.module.ts:
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { IonicModule } from '@ionic/angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app.routing.module';
import { OasisOfTheSeasPage } from './oasis-of-the-seas/oasis-of-the-seas.page';
import { environment } from 'src/environments/environment';
import { Capacitor } from '@capacitor/core';
import { getApp, initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getAuth, indexedDBLocalPersistence, initializeAuth, provideAuth } from '@angular/fire/auth';
import { provideFirestore, getFirestore } from '@angular/fire/firestore';
import { provideStorage, getStorage } from '@angular/fire/storage';
import { provideHttpClient } from '@angular/common/http';

@NgModule({
    imports: [BrowserModule, FormsModule, AppRoutingModule, IonicModule.forRoot({}), 
    ],
    providers: [
      provideHttpClient(),
      provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
       provideAuth(() => {
        if (Capacitor.isNativePlatform()) {
          return initializeAuth(getApp(), {
            persistence: indexedDBLocalPersistence
          });
        } else {
          return getAuth();
        }
      }),
      //getAuth()),
      provideFirestore(() => getFirestore()),
      provideStorage(() => getStorage()),
    ],
    declarations: [AppComponent, OasisOfTheSeasPage],
    bootstrap: [AppComponent],
  })
  export class AppModule {}

reservar.page.html:
<ion-header>
  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>Reservar</ion-title>
  </ion-toolbar>
</ion-header>
<ion-content>
  <form [formGroup]="reservaForm" (ngSubmit)="onSubmit()">
    <ion-item class="titulo" color="primary">
      <ion-title>Fecha de Inicio del Crucero</ion-title>
    </ion-item>
    <ion-datetime-button class="fecha" datetime="datetime" name="fechaInicio" formName="fechaInicio">    </ion-datetime-button>
    <ion-modal [keepContentsMounted]="true">
      <ng-template>
        <ion-datetime id="datetime"></ion-datetime>
      </ng-template>
    </ion-modal>
    <ion-item class="titulo" color="primary">
      <ion-title>Lugares para visitar</ion-title>
    </ion-item>
    <ion-item>
      <ion-select label="Lugares para visitar:" placeholder="Que lugares quieres visitar" name="lugares" formControlName="lugares">
        <ion-select-option value="alaska">Alaska</ion-select-option>
        <ion-select-option value="asia">Asia</ion-select-option>
        <ion-select-option value="australia&nuevazelanda">Australia y Nueva Zelanda</ion-select-option>
        <ion-select-option value="canada&nuevainglaterra">Canada y Nueva Inglaterra</ion-select-option>
        <ion-select-option value="canaldepanama">Canal de Panamá</ion-select-option>
        <ion-select-option value="caribe">El Caribe</ion-select-option>
        <ion-select-option value="europa">Europa</ion-select-option>
        <ion-select-option value="hawai">Hawái</ion-select-option>
        <ion-select-option value="bahamas">Las Bahamas</ion-select-option>
        <ion-select-option value="mexico">México</ion-select-option>
        <ion-select-option value="pacificonoroeste">Pacífico Noroeste</ion-select-option>
        <ion-select-option value="pacificosur">Pacífico Sur</ion-select-option>
        <ion-select-option value="transatlantico">Transatlántico</ion-select-option>
        <ion-select-option value="transpacifico">Transpacífico</ion-select-option>
      </ion-select>
    </ion-item>
    <ion-item class="titulo" color="primary">
      <ion-title>Escoge el puerto de salida</ion-title>
    </ion-item>
    <ion-item>
      <ion-select label="Puerto de Salida:" placeholder="Escoge el puerto de salida" name="puertoSalida" formControlName="puertoSalida">
        <ion-select-option value="barcelona">Barcelona, España</ion-select-option>
        <ion-select-option value="roma">Roma (Civitavecchia), Italia</ion-select-option>
        <ion-select-option value="ravena">Rávena (Venecia), Italia</ion-select-option>
        <ion-select-option value="southampton">Southampton, Inglaterra</ion-select-option>
        <ion-select-option value="baltimore">Baltimore, Maryland</ion-select-option>
        <ion-select-option value="boston">Boston, Massachusetts</ion-select-option>
        <ion-select-option value="capeLiberty">Cape Liberty (Nueva York), Nueva Jersey</ion-select-option>
        <ion-select-option value="fortLauderdale">Fort Lauderdale, Florida</ion-select-option>
        <ion-select-option value="galveston">Galveston, Texas</ion-select-option>
        <ion-select-option value="losAngeles">Los Ángeles, California</ion-select-option>
        <ion-select-option value="miami">Miami, Florida</ion-select-option>
        <ion-select-option value="nuevaOrleans">Nueva Orleans, Luisiana</ion-select-option>
        <ion-select-option value="oahu">Oahu (Honolulú), Hawái</ion-select-option>
        <ion-select-option value="orlando">Orlando (Puerto Cañaveral), Florida</ion-select-option>
        <ion-select-option value="vancouver">Puerto de cruceros de Vancouver, (Columbia Británica)</ion-select-option>
        <ion-select-option value="sanDiego">San Diego, California</ion-select-option>
        <ion-select-option value="seattle">Seattle, Washington</ion-select-option>
        <ion-select-option value="seward">Seward, Alaska</ion-select-option>
        <ion-select-option value="tampa">Tampa, Florida</ion-select-option>
        <ion-select-option value="cartagena">Cartagena, Colombia</ion-select-option>
        <ion-select-option value="colon">Colón, Panamá</ion-select-option>
        <ion-select-option value="sanJuan">San Juan, Puerto Rico</ion-select-option>
        <ion-select-option value="brisbane">Brisbane, Australia</ion-select-option>
        <ion-select-option value="sidney">Sídney, Australia</ion-select-option>
      </ion-select>
    </ion-item>
    <ion-item class="titulo" color="primary">
      <ion-title>Cantidad de noches</ion-title>
    </ion-item>
    <ion-item>
      <ion-input label="Número de huéspedes:" placeholder="Escoge el número de huéspedes" name="huespedes" formName="huespedes"></ion-input>
    </ion-item>
    <div style="text-align: center">Cada 4 huéspedes puedes pedir otro camarote</div>
    <ion-item class="titulo" color="primary">
      <ion-title>Camarotes</ion-title>
    </ion-item>
    <ion-item>
      <ion-select label="Camarote:" placeholder="Escoge un camarote" name="nombreCamarote" formName="nombreCamarote">
        <ion-select-option value="camarotesInteriores">Camarotes Interiores</ion-select-option>
        <ion-select-option value="vistaOceano">Vista al Océano</ion-select-option>
        <ion-select-option value="camaroteBalcon">Camarotes con Balcón</ion-select-option>
        <ion-select-option value="sunsetCornerSuite">Sunser Corner Suite</ion-select-option>
        <ion-select-option value="panoramicSuite">Panoramic Suite</ion-select-option>
        <ion-select-option value="infinityBalconies">Infinity Balconies</ion-select-option>
      </ion-select>
    </ion-item>
    <ion-item class="titulo" color="primary">
      <ion-title>Número de Camarotes</ion-title>
    </ion-item>
    <ion-item>
      <ion-input label="Número de camarotes:" placeholder="Escoge el número de camarotes" name="numeroCamarotes" formName="numeroCamarotes"></ion-input>
    </ion-item>
    <ion-item class="titulo" color="primary">
      <ion-title>Nuestra Flota</ion-title>
    </ion-item>
    <ion-item>
      <ion-select label="Crucero:" placeholder="Escoge un crucero" name="nombreCrucero" formName="nombreCrucero">
        <ion-select-option value="adventure-of-the-seas">Adventure of the Seas</ion-select-option>
        <ion-select-option value="allure-of-the-seas">Allure of the Seas</ion-select-option>
        <ion-select-option value="anthem-of-the-seas">Anthem of the Seas</ion-select-option>
        <ion-select-option value="brilliance-of-the-seas">Brilliance of the Seas</ion-select-option>
        <ion-select-option value="enchantment-of-the-seas">Enchantment of the Seas</ion-select-option>
        <ion-select-option value="explorer-of-the-seas">Explorer of the Seas</ion-select-option>
        <ion-select-option value="freedom-of-the-seas">Freedom of the Seas</ion-select-option>
        <ion-select-option value="grandeur-of-the-seas">Grandeur of the Seas</ion-select-option>
        <ion-select-option value="harmony-of-the-seas">Harmony of the Seas</ion-select-option>
        <ion-select-option value="icon-of-the-seas">Icon of the Seas</ion-select-option>
        <ion-select-option value="independence-of-the-seas">Independence of the Seas</ion-select-option>
        <ion-select-option value="jewel-of-the-seas">Jewel of the Seas</ion-select-option>
        <ion-select-option value="liberty-of-the-seas">Liberty of the Seas</ion-select-option>
        <ion-select-option value="mariner-of-the-seas">Mariner of the Seas</ion-select-option>
        <ion-select-option value="navigator-of-the-seas">Navigator of the Seas</ion-select-option>
        <ion-select-option value="oasis-of-the-seas">Oasis of the Seas</ion-select-option>
        <ion-select-option value="odyssey-of-the-seas">Odyssey of the Seas</ion-select-option>
        <ion-select-option value="ovation-of-the-seas">Ovation of the Seas</ion-select-option>
        <ion-select-option value="quantum-of-the-seas">Quantum of the Seas</ion-select-option>
        <ion-select-option value="radiance-of-the-seas">Radiance of the Seas</ion-select-option>
        <ion-select-option value="rhapsody-of-the-seas">Rhapsody of the Seas</ion-select-option>
        <ion-select-option value="serenade-of-the-seas">Serenade of the Seas</ion-select-option>
        <ion-select-option value="spectrum-of-the-seas">Spectrum of the Seas</ion-select-option>
        <ion-select-option value="star-of-the-seas">Star of the Seas</ion-select-option>
        <ion-select-option value="symphony-of-the-seas">Symphony of the Seas</ion-select-option>
        <ion-select-option value="utopia-of-the-seas">Utopia of the Seas</ion-select-option>
        <ion-select-option value="vision-of-the-seas">Vision of the Seas</ion-select-option>
        <ion-select-option value="voyager-of-the-seas">Voyager of the Seas</ion-select-option>
        <ion-select-option value="wonder-of-the-seas">Wonder of the Seas</ion-select-option>
      </ion-select>
    </ion-item>
    <div class="boton">
      <ion-button type="submit">Reservar</ion-button>
    </div>
  </form>
</ion-content>


I expect that when i press reservar button it saves all the data in firebase

Solution

  • You are mixing standalone and modular approaches of angular. You should use either one, either app.module.ts or direct bootstrap using bootstrapApplication in main.ts.

    You should go through the docs about these two approaches. I have copied the providers from app.modules.ts to main.ts.

    Angular Modules

    Migrate an existing Angular project to standalone

    The problem you are facing is you have missed to add provideFirestore(() => getFirestore()).

    You can delete app.module.ts since your application seems to use standalone approach.

    import { bootstrapApplication } from '@angular/platform-browser';
    import { RouteReuseStrategy, provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
    import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
    
    import { routes } from './app/app.routes';
    import { AppComponent } from './app/app.component';
    import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
    import { getAuth, provideAuth } from '@angular/fire/auth';
    
    bootstrapApplication(AppComponent, {
      providers: [
         provideHttpClient(),
         { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
         provideIonicAngular(),
         provideAuth(() => getAuth()),
         provideFirestore(() => getFirestore()),
         provideRouter(routes, withPreloading(PreloadAllModules)), provideFirebaseApp(() => initializeApp({
           ...
         })), 
         provideAuth(() => getAuth()),
      ],
    });