Неперехваченная ошибка FirebaseError: функция DocumentReference.set() вызывается с недопустимыми данными, данные должны быть объектом

#javascript #arrays #json #firebase #google-cloud-firestore

#javascript #массивы #json #firebase #google-cloud-firestore

Вопрос:

В настоящее время я пытаюсь составить анкету, в которой пользователи оценивают изображение. Я хочу передать значение, которое пользователь оценил изображению, наряду с изображением, которое они оценили, в базу данных. В настоящее время я печатаю это в журнале консоли, но у меня возникли проблемы с отправкой его в базу данных. В нем говорится, что данные переключателя не являются объектом. Я впервые использую 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>  

Комментарии:

1. Вы пытаетесь записать строку src изображения в базу данных без указания какого-либо ключа. На самом деле вы не пытаетесь обновить коллекцию фактическим документом. В вашем конкретном случае может иметь смысл иметь документ с вызываемым ключом images , возможно, и чтобы этот ключ содержал истинный массив. Затем при обновлении вы считываете массив и [...arr, selected] добавляете его в этот массив.

2. да, я рад записать строку src изображения в базу данных, чтобы я мог определить скорость для этого изображения! как мне сделать то, что вы предлагаете?

Ответ №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 атрибут к каждой радиокнопке. Это понадобится нам позже, чтобы мы знали, какой документ с изображением мы обновляем!

Затем, позже, когда вы добавляете рейтинг для этого изображения, вы просто ссылаетесь на это конкретное изображение и добавляете выбранный рейтинг в массив:

 // 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 где-нибудь.

Это очень многословный способ сказать: «Да, это очень выполнимо, но это чертовски сложная тема». У меня есть копия этой работы: https://repl.it/@TobiasParent/Firestore-image-doc

Комментарии:

1. спасибо вам за все это! в итоге я решил проблему, сохранив имя изображения и номинальное значение в словаре и передал его в firebase

Ответ №2:

Решаемая путем добавления имени изображения и номинального значения в словарь