Casting firestore observables to custom objects

↘锁芯ラ 提交于 2021-02-07 19:56:27

问题


I'm new to angular and firestore and trying to figure out how to cast the data received from firebase directly to models. What is the best approach here?

Currently I get the data, but it looks like it's not casted into a Blimp object. When I try to call getImageUrl() on it in the view, I get the following error message.

ERROR TypeError: _v.context.$implicit.getImageUrl is not a function

So my question: What is the best and cleanest way to cast these observables to the correct local model? I was expecting the tags to cast it by default.

Current code

Custom model class

export class Blimp {


created_at: Date;
file_location: string;
id: string;

constructor() {
    console.log('OBJ');
}

getImageUrl() {
    return "https://*.com" + this.file_location;
}

}

Service class

    import { Injectable } from '@angular/core';
import {Blimp} from '../models/blimp';
import { AngularFirestore } from '@angular/fire/firestore';
import {AngularFireStorage, AngularFireUploadTask} from '@angular/fire/storage';
import {Observable} from 'rxjs';
import {finalize} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class BlimpService {

   blimps: Observable<Blimp[]>;

  constructor(private fireStore: AngularFirestore, private fireDisk: AngularFireStorage) { }


  getBlimps() {
      this.blimps = this.fireStore.collection<Blimp>('blimps').valueChanges();

      return this.blimps;
  }
}

Display component

import { Component, OnInit } from '@angular/core';
import {BlimpService} from '../../services/blimp.service';
import {Observable} from 'rxjs';
import {Blimp} from '../../models/blimp';

@Component({
  selector: 'app-blimp-viewer',
  templateUrl: './blimp-viewer.component.html',
  styleUrls: ['./blimp-viewer.component.scss'],
})
export class BlimpViewerComponent implements OnInit {

  blimps: Observable<Blimp[]>;

  constructor(private blimpService: BlimpService) { }

  ngOnInit() {
    this.blimps = this.blimpService.getBlimps();
  }

}

View

<ul>
  <li *ngFor="let blimp of blimps | async">
      {{ blimp | json}}
      <img [src]="blimp.getImageUrl()" />
  </li>
</ul>

Update #1

Changed the code to

I now have changed your example to: getBlimps() {

   this.blimps = this.fireStore.collection<Blimp>('blimps')
          .valueChanges()
            pipe(map(b => {
                let blimp = new Blimp();
                blimp.created_at = b.created_at;
                blimp.file_location = b.file_location;
                blimp.id = b.id;

                return blimp;

            }));

      return this.blimps;
  }

This still complains in the view about the getImageUrl() not being found on the object.

# Solution

Looks like I forget a . (dot) in the last code

This code works:

this.blimps = this.fireStore.collection<Blimp>('blimps')
      .valueChanges()
      .pipe(map(collection => {
            return collection.map(b => {
                let blimp = new Blimp();
                blimp.created_at = b.created_at;
                blimp.file_location = b.file_location;
                blimp.id = b.id;

                return blimp;
            });
        }));

  return this.blimps;

回答1:


Concept :

You don't cast an observable to an object model. An observable is a stream which has a lifecycle.
An observable emits value to its subscribers, you need to subscribe to your observable to be notified when it emits value. You also need to close the subscription or the subscription will last until your observable complete causing memory leaks.
I can see you're using | asyncin your html template, it's a subscription handled by angular that auto-unsubscribe when needed.

Get data :

You need to map the data you received to a Blimp object, you can use map operator.

blimps$: Observable<Blimp[]>; // naming convention, suffix your observable with $



blimps$ = this.fireStore.collection<Blimp>('blimps')
          .valueChanges()
          .pipe(map(collection => {
                return collection.map(b => {
                    let blimp = new Blimp();
                    blimp.created_at = b.created_at;
                    blimp.file_location = b.file_location;
                    blimp.id = b.id;
                    console.log(blimp);
                    console.log(b);
                    return blimp;
                });
            }));

      return this.blimps;

As we changed blimps to blimps$, change your html template :

*ngFor="let blimp of blimps$ | async"

EDIT :

You can use your class constructor to initialize your object :

export class Blimp {

created_at?: Date;
file_location?: string;
id?: string;

constructor(blimp: Blimp = {}) {
  this.created_at = blimp.created_at;
  this.file_location = blimp.file_location;
  this.id = blimp.id;
}

getImageUrl() {
    return `https://*.com${this.file_location}`; // use string interpolation here
}



blimps$ = this.fireStore.collection<Blimp>('blimps')
              .valueChanges()
              .pipe(map(collection => {
                    return collection.map(b =>  new Blimp(b));
                }));


来源:https://stackoverflow.com/questions/57304439/casting-firestore-observables-to-custom-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!