Firestore: Add Custom Object to db

2019-03-24 18:13发布

Good Morning,

I tried adding a new created object from this class:

export class Sponsor implements ISponsor {

  title: string;    
  description?: string;
  creation: ICreation;

  constructor(title: string, description: string, author: string) {
     this.title = title;
     this.description = description;
     this.creation = new Creation(author);
  }
}

and in my service, the create function looks like:

createSponsor(sponsor) {
   sponsor.id = this.afs.createId();
   return this.collection.doc(sponsor.id).set(sponsor);
}

When I try it this way, I get the following error:

FirebaseError: [code=invalid-argument]: Function DocumentReference.set() called with invalid data. Data must be an object, but it was: a custom Sponsor object

How can I solve this issue?

7条回答
贪生不怕死
2楼-- · 2019-03-24 18:41

Thx to Fabian Wiles - I got it!

while firebase could send the data inside your object to the database, when the data comss back it cannot instantiate it back into an instance of your class. Therefore classes are disallowed

just save an object like this:

interface Person{
  name: string;
  age: number
}

var person: Person = { name: 'Toxicable', age: 22} ;
查看更多
你好瞎i
3楼-- · 2019-03-24 18:42

It's really strange behavior from firebase. And that how I fixed it - by creating new Interface and adding convertation method to my class:

export class Happening {
 constructor(
  public date: EventDate,
  public participants: Array<string>,
  public title: string,
  public text: string,
  public uid?: string,
  public id?: string
 ){}

 public toDto = (): HappeningDto => {
  return {
    date: {
      year: this.date.year,
      month: this.date.month,
      day: this.date.day
    },
    participants: this.participants ? this.participants : [],
    title: this.title,
    text: this.text ? this.text : '',
    uid: this.uid,
    id: this.id ? this.id : null
  }
 }
}

export interface HappeningDto {
 date: {
  year: number,
  month: number,
  day: number
 },
 participants: Array<string>,
 title: string,
 text: string,
 uid?: string,
 id?: string
}

Now, I can do

add(event: Happening){
  event.uid = this.uid;
  this.$afs.collection<HappeningDto>('events').add(event.toDto())
    .then(
      (success) => console.log(success),
      (err) => console.warn(err)
    )
}
查看更多
Lonely孤独者°
4楼-- · 2019-03-24 18:46

If you use Angular and AngularFire2, you can use AngularFirestype. This module is meant to replace AngularFirestore and allow to get and set data to Firestore directly with custom objects.

To do so, 3 steps are required:

1. Install angular-firestype

`npm install angular-firestype --save`

2. Initialize AngularFirestype module with a mapping object

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { AngularFirestypeModule, ModelType } from 'angular-firestype';
import { environment } from '../environments/environment';

import { User } from './user.ts';
import { Address } from './address.ts';
import { Message } from './message.ts';

/**
 * Definition of the app model mapping.
 * For more information, see https://github.com/bricepepin/angular-firestype#mapping-object.
 */
const model: {[key: string]: ModelType<any>} = {
  users: {
    type: User,
    arguments: ['username', 'image'],
    structure: {
      adress: Address
    },
    subcollections: {
      messages: Message
    }
  }
};

@NgModule({
 imports: [
   AngularFireModule.initializeApp(environment.firebase),
   AngularFireAuthModule,
   AngularFirestypeModule.forRoot(model),   // Import module using forRoot() to add mapping information
 ],
 declarations: [ AppComponent ],
 bootstrap: [ AppComponent ]
})
export class AppModule {}

3. Inject AngularFirestype service

import { Component } from '@angular/core';
import { AngularFirestype, Collection, Document } from 'angular-firestype';

import { User } from './user.ts';

@Component({
 selector: 'app-root',
 templateUrl: 'app.component.html',
 styleUrls: ['app.component.css']
})
export class AppComponent {
   const users: Observable<User[]>;
   const user: User;

   constructor(db: AngularFirestype) {
       const usersCollection: Collection<User> = db.collection<User>('users');
       usersCollection.valueChanges().subscribe(users => this.users = users);

       const userDoc: Document<User> = usersCollection.doc('user1');
       userDoc.valueChanges().subscribe(user => this.user = user);
       userDoc.set(this.user);
   }
}

You can basically use AngularFirestype like you use Angularfirestore.
For more details, see the homepage here: https://github.com/bricepepin/angular-firestype.

查看更多
ら.Afraid
5楼-- · 2019-03-24 18:50

You could also serialize your object into JSON and deserialize it back into a regular JavaScript object like

this.collection.doc(sponsor.id).set(JSON.parse( JSON.stringify(sponsor)));

works with deep nesting.

查看更多
劳资没心,怎么记你
6楼-- · 2019-03-24 18:51

Firestore does not support that. But you can use https://github.com/typestack/class-transformer It works perfectly fine for us.

查看更多
干净又极端
7楼-- · 2019-03-24 18:55

You can also use Object.assign({}, sponsor)

so in yor case it would be

this.collection.doc(sponsor.id).set(Object.assign({}, sponsor));
查看更多
登录 后发表回答