文档引用.set()函数调用了无效数据。不支持的字段值:自定义预算对象。

68

以下代码一直运行良好,但今天不知道为什么无法工作,并出现以下错误。你能告诉我原因吗?

Error: Function DocumentReference.set() called with invalid data. Unsupported field value: a custom Budget object

 export class Project {
        id: string = null;
        name: string;
        budgetList?: Budget[];
    }

export class Budget {
    id: string;
    amount: number;
    contingency: number = 20;
    budgetGroup: BudgetGroup = new BudgetGroup();
    creationTime: string;
}

代码:

  async create(data: DtoProject): Promise<Project> {
    try {
      const projectId: string = this.fireStore.createId();
      const budgets = this.budgetProvider.createBudgets(data.budgetList, projectId);//budgets
      const proj: Project = {
        id: data.id,
        name: data.name,
        budgetList: budgets,//here it has the error
      }
      proj.id = projectId;
      await this.fireStore.doc<Project>(`projects/${projectId}/`).set(proj));//project
      }
 }

  createBudgets(data: Budget[], projectId: string): Budget[] {
    let budgets: Budget[] = [];
    forEach(data, (d) => {
      const budgetId: string = this.fireStore.createId();
      d.id = budgetId;
      budgets.push(d);
      this.fireStore.doc<Budget>(`projects/${projectId}/budgets/${budgetId}`).set({
        id: budgetId,
        amount: d.amount,
        contingency: d.contingency,
        budgetGroup: d.budgetGroup,
        creationTime: moment().format()
      })
    })
    return budgets;
  }

3
在传入一个 JSON 对象时,遇到了一个错误,其中一些字段的值意外地为 null。 - Gene Bo
8个回答

107
你需要将预算数组转换为纯JavaScript对象的数组。
第一步:
const budgets = arrayOfBudget.map((obj)=> {return Object.assign({}, obj)});

第二步:

const proj: Project = {
      id: data.id,
      name: data.name,
      budgetList: budgets
    }

那么你就可以开始使用它了。

顺便说一下,如果使用编译成JavaScript的语言进行开发,你不能使用自定义对象,而必须使用纯JavaScript对象保存在Firestore数据库中。

例如,假设你有以下类:

export class User {
        id: string;
        name: string;
    }

如果你尝试执行以下代码:

 const user = new User();   
 this.db.collection('users').doc().set(user)

你会得到如下错误:

无效数据。数据必须是一个对象,但它却是:一个自定义的用户对象

现在如果你尝试执行这另一行代码:
 this.db.collection('users').doc().set(Object.assign({}, user))

您将看到您的对象已保存在数据库中。基本上,Object.assign 做的事情与以下代码相同:

this.db.collection('users').doc().set({id: user.id , name: user.name})

使用 Object.assign,可以节省很多时间。

更新

正如我在下面的评论中指出的那样,您可以在这里找到有关自定义对象的文档。如您所见,警告显示为:

//Web 使用 JavaScript 对象

下面是文档的屏幕截图:

enter image description here


2
非常好,它完美地运行了。非常感谢。但是你能告诉我为什么有些数组可以工作而有些不行吗?例如,我在同一个类中有另一个数组,像这样。但那里没有错误。有任何解释吗?我需要像你上面展示的那样转换它吗?export class Project { memberList?: Member[]; } - Sampath
4
由于我没有看到您代码的其余部分,很难确定。但我强烈建议您始终将对象转换为纯JavaScript对象。 - Mateus Forgiarini da Silva
1
不用谢。我很高兴能帮到你 :)您可以查看有关自定义对象的文档。 这是链接: https://firebase.google.com/docs/firestore/manage-data/add-data正如您所看到的,有一个警告:“Web使用JavaScript对象”。因此,请使用Object.assign。 - Mateus Forgiarini da Silva
如果您有一个GeoPoint类型的字段,并且想要保留该类型,该怎么办? - bvamos
为了避免一次又一次地进行“对象到原始JS对象”的转换,现在有一个数据转换器类,您可以在保存和检索时使用该类来触发所述的转换。参考:https://firebase.google.com/docs/reference/js/firebase.firestore.FirestoreDataConverter - Gregordy

23
您可以将对象转换为字符串,然后再解析为对象以删除对象的类名。
const budgets = this.budgetProvider.createBudgets(JSON.parse(JSON.stringify(data.budgetList)), projectId);

1
我尝试了其他的例子,但这个对我来说是完美地与angularfire2配合使用的。 - 今際のアリス
我猜这也可能是修复我的代码中使用JS类user{}时出现的错误的方法。这是对我有效的解决方案,但似乎很奇怪需要这样做。 - raddevus

9

您也可以使用扩展运算符,例如:

this.fireStore
  .doc<Project>(`projects/${projectId}/`)
    .set( {...proj} ))

它对我有用


3
这绝对是最简单的解决方法,我只需要付出很少的努力就能解决问题。 - NeonNatureEX
2
{ ...proj } 进行的是浅复制,因此该方法仅适用于 proj 包含基本类型而非其他类型的引用。 - VeganHunter

2
您还可以使用add方法,并使用展开运算符将所有值复制到新对象中。这对我很有效: ```javascript add({...object}) ```
const user = new User();   
this.db.collection('users').add({...user});

0

当我尝试直接将地图添加到Firestore(v9)时,我遇到了相同的问题。

问题是:


// Firestore module v9

// I was trying to add a Map to firestore

        userInterestsMap.forEach(async (v: Map<string, number>, k: string) => {
            const modelRef = doc(db, "model", k);
            await setDoc(modelRef, {
                'data': v // here is the issue
            });
        })

解决方案

        userInterestsMap.forEach(async (v: Map<string, number>, k: string) => {
            const modelRef = doc(db, "model", k);
            await setDoc(modelRef, {
                'data': Object.fromEntries(v) // here is the solution
            });
        })

所以,基本上是将Map转换为Object


0
对于任何使用FirestoreDataConverter的人,这是我在Recipe对象内部的RecipeTag对象数组上如何实现它的方法。

//调用添加食谱的位置

        const collectionRef = collection(db, collectionPath)
                             .withConverter(Recipe.firestoreConverter);
        await addDoc(collectionRef, recipe);

//食谱模型

    public static firestoreConverter: FirestoreDataConverter<Recipe> = {
        toFirestore: (recipe: Recipe) => {
            return {
                title: recipe.title,
                recipeUrl: recipe.recipeUrl,
                imageLink: recipe.imageLink,
                tags: recipe.tags.map(tag => RecipeTag.firestoreConverter.toFirestore(tag))
            };
        },
        fromFirestore: (snapshot: DocumentSnapshot<DocumentData>) => {
            const data = snapshot.data() as Recipe
            return new Recipe(data)
        }
    }

//RecipeTag 模型

    public static firestoreConverter: FirestoreDataConverter<RecipeTag> = {
        toFirestore: (recipe: RecipeTag) => {
            return {
                description: recipe.description,
                title: recipe.title,
            };
        },
        fromFirestore: (snapshot: DocumentSnapshot<DocumentData>) => {
            const data = snapshot.data() as RecipeTag
            return new RecipeTag(data)
        }
    }

0

这对我有效

proj.budgetList = {...budgets}


await this.fireStore.doc<Project>(`projects/${projectId}/`).set({...proj}));

这是JSON.parse(JSON.stringify(budgets))的一个不错的替代方案(如果字段未定义,则将其添加到对象中(这是区别所在),并且fb会抛出错误-这可能很有用)。 - Kamil Kiełczewski

-2

错误只是说明输入的数据无效!

我也曾经不知道从何而来地遇到了这个错误,后来才发现是因为我从界面中删除了应该获取值并将其上传到某个集合文档的输入字段!

注意:检查您在.ts文件中的代码firebase.firestore().collection("some_name").add() / set()是否存在不必要的字段。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接