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

AngularTypescriptFirebaseIonic3Google Cloud-Firestore

Angular Problem Overview


Below code works fine until today. But I don't know now it is not working and gives below error.Could you tell me why?

> 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;
}

code:

  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;
  }

Angular Solutions


Solution 1 - Angular

You have to transform your array of budgets into an array of pure JavaScript objects.

First step:

const budgets = arrayOfBudget.map((obj)=> {return Object.assign({}, obj)});

Second step:

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

Then you are good to go.

By the way, when developing with a language that compiles to JavaScript you cannot use custom Objects. Instead, you have to use pure JavaScript objects to save in the Firestore Database.

For example, let's say you have this class below:

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

And you try to execute the following code:

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

You will get an error like:

> invalid data. Data must be an object, but it was: a custom User object

Now if you try to execute this other line of code:

 this.db.collection('users').doc().set(Object.assign({}, user))

You will see that your object was saved in the database. Basically Object.assign does the same thing as:

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

So make use of Object.assign, it will save you a lot of time.

UPDATE

As I have pointed out in a comment below, you can find what documentation says about Custom objects here. As you can see, there is a warning saying:

> // Web uses JavaScript objects

Below there is a screenshot of what the documentation says.

enter image description here

Solution 2 - Angular

You can stringify the object and parse to object again to remove class names of the object.

const budgets = this.budgetProvider.createBudgets(JSON.parse(JSON.stringify(data.budgetList)), projectId);

Solution 3 - Angular

You can also use the spread operator like:

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

It work for me

Solution 4 - Angular

You can also use the add method with a new object copying all your values with the spread operator. This worked for me:

const user = new User();   
this.db.collection('users').add({...user});

Solution 5 - Angular

It work for me

proj.budgetList = {...budgets}


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

Solution 6 - Angular

To anyone using FirestoreDataConverter here's how I did it for an array of object type RecipeTag inside an object of type Recipe.

//Location where I call to add a Recipe

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

//Recipe model

	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 model

	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)
		}
	}

Solution 7 - Angular

Error is simply stating that data entered is invalid!

I've also faced the error from nowhere, which was because I removed the input field from interface which was supposed to get it's value and upload it in the document of some collection!

note: check your code in .ts file firebase.firestore().collection("some_name").add() / set() if some unnecessary field is there or not.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSampathView Question on Stackoverflow
Solution 1 - AngularMateus Forgiarini da SilvaView Answer on Stackoverflow
Solution 2 - AngularMalindaKView Answer on Stackoverflow
Solution 3 - AngularAndres GardiolView Answer on Stackoverflow
Solution 4 - AngularJose RosarioView Answer on Stackoverflow
Solution 5 - Angularידידיה מלריךView Answer on Stackoverflow
Solution 6 - AngularKarolis VaitkeviciusView Answer on Stackoverflow
Solution 7 - AngularVaibhav DewanganView Answer on Stackoverflow