【问题标题】:Uncaught FirebaseError: Function DocumentReference.set() called with invalid data, Data must be an object未捕获的 FirebaseError:使用无效数据调用的函数 DocumentReference.set(),数据必须是一个对象
【发布时间】:2021-02-26 09:15:00
【问题描述】:

我目前正在尝试制作一份调查问卷,让用户对图像进行评分。我想将用户评价图像的值与他们评价的图像一起推送到数据库。我目前正在将其打印到控制台日志,但无法将其发送到数据库。它表示单选按钮的数据不是对象。这是我第一次使用firebase,对JS的经验很少。

function validate() {
    var radio = document.getElementsByName("answer");
    var checked = false;
    for (var i = 0; i < radio.length; i++) {
        if (radio[i].checked) {
            checked = true;
            break;
        } else {
            checked = false;
        }
    }
    if (checked) {
        for (var i = 0; i < radio.length; i++)
        {
            if (radio[i].checked)
            {
                selected = (radio[i].value)
                window.sessionStorage.setItem("selected", JSON.stringify(selected));
                console.log(selected)
                changeImage();
            }
        } 
        
    } else {
        alert("You must select an answer!")
    }
} 

 
var currentImage = 0;
var imageArray = ["/Users/rate/images/images/practice/sun1.jpg", "/Users/rate/images/images/practice/sun2.jpg", "/Users/rate/images/images/practice/sun3.jpg", "/Users/rate/images/images/practice/sun4.jpg"]
function changeImage(){
    if (currentImage >= imageArray.length - 1) {
        writeToDB();
      } else {
        currentImage += 1;
      }
    document.getElementById("mainImage").src = imageArray[currentImage]
    img = imageArray[currentImage].slice(-8)
    console.log(img)
    window.sessionStorage.setItem("selected", JSON.stringify(img));
}



function writeToDB() {

    var selected = JSON.parse(window.sessionStorage.getItem("selected"));

    var firebaseConfig = {
        apiKey: "***",
        authDomain: "**",
        databaseURL: "**",
        projectId: "**",
        storageBucket: "**",
        messagingSenderId: "**",
        appId: "**",
        measurementId: "**"
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
    var db = firebase.firestore();
    db.collection("selected").doc().set(selected)
        .then(function () {
            console.log("Document successfully written!");
        })
        .catch(function (error) {
            console.error("Error writing document: ", error);
        });
}
<!DOCTYPE html>

<html>

<head>
    <title> App Icons </title>
    <link rel="stylesheet" type="text/css" href="/Users/rate/css/mainstyle.css">
    <script type="text/javascript" src="/Users/rate/js/main.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-analytics.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-database.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-firestore.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-storage.js"></script>
</head>



<body class="body">
    <div>
        <div>
            <hr>
            <div class=topbar>
                <h1> Do you find this App Icon aesthically pleasing</h1>
            </div>
            <hr>

            <div class=image> 
            <img src="/Users/rate/images/images/practice/sun1.jpg" id="mainImage"/>
            </div>
            <hr>

            <div class=answer> 
            <div id=answer>
                <label> Strongly Agree </label>
                <label><input type="radio" name="answer" value="1" required><span>1</span></label>
                <label><input type="radio" name="answer" value="2" required><span>2</span></label>
                <label><input type="radio" name="answer" value="3" required><span>3</span></label>
                <label><input type="radio" name="answer" value="4" required><span>4</span></label>
                <label><input type="radio" name="answer" value="5" required><span>5</span></label>
                <label> Strongly disagree</label>
                <hr>
            </div>



            </div>
            
            <div class=complete> 
                <input type="button" onclick="validate()" value="I am complete">
            </div>
</body>

【问题讨论】:

  • 您正在尝试将图像 src 字符串写入数据库,但没有识别任何类型的键。实际上,您并没有尝试使用实际文档来更新集合。在您的特定情况下,拥有一个带有名为images 的键的文档可能是有意义的,并且让该键包含一个真正的数组。然后,在更新中,您读取数组并[...arr, selected] 将其添加到该数组中。
  • 是的,我很高兴将图像 src 字符串写入数据库,以便确定该图像的速率!我会怎么做你的建议?

标签: javascript arrays json firebase google-cloud-firestore


【解决方案1】:

所以答案有点复杂,因为您希望拥有比实际告诉数据库更多的数据。每个文档的数据结构可能更像这样:

{
  url: /* string containing image's url */,
  rating: [4, 2, 5, 0] /* array containing ratings given for this image
}

每张图片应该以大致相同的方式表示 - 所以如果你有一个名为 images 的集合,那么你可能会有这样的东西:

const dbRef = firebase.firestore();
const imagesRef = dbRef.collection('images');

const oneImage = imagesRef.doc('JdBPHMYWfNMMlMpfsjv5');
oneImage.get().then(function(doc){
  console.log( doc.data() )
  // that might return 
  {
    'url': '/img/sun002.jpg',
    'rating': [5,2,3,2,0,5]
  }

  /***
   * but! In your case, you'd likely want to create the HTML fragment
   * you'll be injecting for this particular image:
   ***/
  // so let's pull all the stuff out of the database, and create an object
  //  that includes the image's id.
  const image = {id: doc.id, ...doc.data() }

  // Using that, we can create an HTML fragment that generates the radio buttons
  const imageEl = document.createRange().createContextualFragment(`<div class='img-container'>
  <img src='${image.url}'>
  <fieldset class='image-rating'>Rate this image: 
    <label>0 <input type='radio' name='rating' data-uid='${image.id}' value=0 /></label>
    <label>1 <input type='radio' name='rating' data-uid='${image.id}' value=1 /></label>
    <label>2 <input type='radio' name='rating' data-uid='${image.id}' value=2 /></label>
    <label>3 <input type='radio' name='rating' data-uid='${image.id}' value=3 /></label>
    <label>4 <input type='radio' name='rating' data-uid='${image.id}' value=4 /></label>
    <label>5 <input type='radio' name='rating' data-uid='${image.id}' value=5 /></label>
  </fieldset>
</div>
'`)

// Let's add a listener for the click on the wrapper for all these radios
imageEl.querySelector('.image-rating').addEventListener('click', handleRatingClick)

// And finally, let's add this image block to the page!
document.querySelector("#images-content-pane").appendChild(imageEl)

})

请注意,每个图像在集合中都有自己的doc,但它可以包含您可能喜欢的任何属性。你可以添加一个caption 属性,或者任何对你有用的东西。

但有几点需要注意:

  1. 我正在以编程方式创建每个图像的容器,并将侦听器动态添加到每个图像的字段集中。
  2. 我正在为每个单选按钮添加一个data-uid 属性。我们稍后需要它,所以我们知道我们正在更新哪个图像文档!

然后,稍后,当您为 this 图像添加评分时,您只需引用此特定图像并将所选评分添加到数组中:

// In my case, I had a fieldset wrap all my radio buttons, and a click was handled there:
const handleRatingClick = (event) =>{
  // First, let's get the clicked el:
  const ratingBtn = event.target;
  const imageId = ratingBtn.dataset.uid;
  const rating = ratingBtn.value;


  // And here I update the array in the rating property, by adding the given value
  imagesRef.doc(imageId).update({
    rating: firebase.firestore.FieldValue.arrayUnion(Number(rating))
  })

}

请注意,这不是唯一的方法,甚至不是正确的方法。关键是,您需要将每个图像视为数据库中的一个文档,并且在创建或更新它时需要为 firestore 提供一个对象。

可以按照您的方式操作,并使用 localStorage/sessionStorage 中的图像,但即便如此,sessionStorage 中的值也需要是一个对象,包含

{
  url,
  answer
}

并且使用 .set() 每次都会简单地覆盖 answer 的值,而不是随着时间的推移存储它们。此外,为了让您的工作正常,您需要在某处存储一个 doc.id。

这是一种非常啰嗦的说法:“是的,这是非常可行的,但这是一个非常高级的话题。”我确实有这个工作的repl:https://repl.it/@TobiasParent/Firestore-image-doc

【讨论】:

  • 感谢您所做的一切!我最终解决了这个问题,通过将图像名称和额定值存储在字典中并将其传递给 firebase
【解决方案2】:

通过将图像名称和额定值添加到字典中解决

【讨论】:

    猜你喜欢
    • 2021-08-29
    • 2018-10-10
    • 2019-12-07
    • 2020-04-18
    • 2022-07-20
    • 2022-11-12
    • 2020-04-24
    • 2023-03-24
    相关资源
    最近更新 更多