问题
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 | async
in 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