简体   繁体   中英

Angular Firebase how to retrieve single object from AngularFirestoreCollection

I'm building an application with Angular for client side and Firebase for back-end. What I'm trying to do is to retrieve a single object from AngularFirestore. But unfortunately the server response with a empty array and with

Object { initialTeardown: undefined, closed: false, _parentage: null, _finalizers: (1) […], isStopped: false, destination: {…} }

This is my estateService file, all other methods works only the method getById() didn't works as expected

import { Injectable } from '@angular/core';
import {
  AngularFirestore,
  AngularFirestoreCollection,
} from '@angular/fire/compat/firestore';
import { Estate } from '../models/estate.model';

@Injectable({
  providedIn: 'root',
})
export class EstateService {
  private dbPath = '/estates';

  estatesRef: AngularFirestoreCollection<Estate>;

  constructor(private db: AngularFirestore) {
    this.estatesRef = db.collection(this.dbPath);
  }

  getAll(): AngularFirestoreCollection<Estate> {
    return this.estatesRef;
  }

  getById(estateId: string): AngularFirestoreCollection<Estate> {
    return this.db.collection<Estate>('estates', ref => ref.where('id', '==', estateId));
  }

  getByUser(userId: string): AngularFirestoreCollection<Estate> {
    return this.db.collection<Estate>('estates', ref => ref.where('owner', '==', userId));
  }

  create(estate: Estate): any {
    return this.estatesRef.add({...estate});
  }

  update(id: string, data: any): Promise<void> {
    return this.estatesRef.doc(id).update(data);
  }

  delete(id: string): Promise<void> {
    return this.estatesRef.doc(id).delete();
  }

}

And this is the estate-details component where I'm trying to implement and use the single object.

  @Component({
  selector: 'app-estate-details',
  templateUrl: './estate-details.component.html',
  styleUrls: ['./estate-details.component.scss'],
})
export class EstateDetailsComponent {
  estate: Estate | undefined;

  constructor(
    private estateService: EstateService,
    private activatedRoute: ActivatedRoute
  ) {
    this.fetchSingleEstate();
  }

  fetchSingleEstate(): void {
    this.estate = undefined;
    const estateId = this.activatedRoute.snapshot.params['id'];
    const estateAr = this.estateService.getById(estateId);
    console.log(
      estateAr.snapshotChanges().pipe(
        map((changes) =>
          changes.map((e) => ({
            ...(e.payload.doc.data() as Estate),
            id: e.payload.doc.id,
          }))
        )
      ).subscribe((data) => {
        console.log(data);
      })
    );
  }
}

this.activatedRoute.snapshot.params['id'] returns the correct Id. I searched for a information but  didn't find any solution that works in my case. 

All checked solutions: Angular | Get one row from Firebase , How i can get single record with ID , How to get one doc by id , Docs

Any help will be appreciated! Thank you in advance!

After a long research the following do the job for me. Instead of new request to the server I've created a setActive() method in the objects list component. And whit this method I'm just taking the currentObject and the currentObjectIndex .

@Component({
  selector: 'app-estates',
  templateUrl: './estates.component.html',
  styleUrls: ['./estates.component.scss'],
  animations: [pageAnimations]
})
export class EstatesComponent implements OnInit {
  arrayEstate?: Estate[];

  currentEstate?: Estate;
  currentIndex = -1;

  constructor(
    private estateService: EstateService,
    private authService: AuthService
  ) {
    this.retrieveEstates()
  }

  ngOnInit(): void {
  }

  refreshList(): void { // Used to set the active estate to null
    this.currentEstate = undefined;
    this.currentIndex = -1;
    this.retrieveEstates();
  }

  setActiveEstate(estate: Estate, index: number): void {
    this.currentEstate = estate;
    this.currentIndex = index;
  }

}

And just passing the current estate to the child component(Which in my case is EstateDetailsComponent) using the @Input decorator. And with the help of the OnChanges lifecycle hook just listen for changes in the currentObject value. If any changes just pass them to the currentEstate property in my child component.

@Component({
  selector: 'app-estate-details',
  templateUrl: './estate-details.component.html',
  styleUrls: ['./estate-details.component.scss'],
  // animations: [bounceIn],
})
export class EstateDetailsComponent implements OnInit {
  @Input() estate?: Estate;
  @Output() refreshList: EventEmitter<any> = new EventEmitter();
  @ViewChild(NgForm, { static: true }) form!: ElementRef<HTMLInputElement>;

  currentEstate: Estate = {
    address: '',
    city: '',
    zip: '',
    bedrooms: 0,
    baths: 0,
    type: '',
    price: 0,
    rented: true,
    maintenance: false,
  };

  constructor(private estateService: EstateService) {}

  ngOnChanges(): void {
    this.currentEstate = { ...this.estate };
  }

  updateEstate(form: NgForm): void { //Edit single object
    const data = {
      id: this.currentEstate.id,
      owner: this.currentEstate.owner,
      ...form.value,
    };

    if (this.currentEstate.id) {
      this.estateService
        .update(this.currentEstate.id, data)
        .then(() => this.toggleEditMode())
        .catch((err) => console.log(err));
    }
  }


  unsetEstate(): void {
    this.refreshList.emit();
  }

}

This is a way more flexible than taking the single object with server request. Hope someone else find this helpful too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM