简体   繁体   中英

how should i dynamically update a firebase document

i'm working on a simple note-taking app for my portfolio using JS and Firebase. Before i tell you what's happening i feel like i need to show you how my code works, if you have any tips and concerns please tell me as it would be GREATLY appreciated. That being said, let's have a look "together". I'm using this class to create the notes:

 const htmlElements = [document.querySelector('.notes'), document.querySelector('.note')];
 const [notesDiv, noteDiv] = htmlElements;
 
 class CreateNote {
   constructor(title, body) {
     this.title = title;
     this.body = body;
     this.render = () => {
     const div1 = document.createElement('div');
     div1.className = 'notes-prev-container';

     div1.addEventListener('click', () => { this.clickHandler(this) });

     const div2 = document.createElement('div');
     div2.className = 'notes-prev';

     const hr = document.createElement('hr');
     hr.className = 'notes__line';

     // Nest 'div2' inside 'div1'

     div1.appendChild(div2);
     div1.appendChild(hr);

    /*
       Create Paragraph 1 & 2 and give them the same
       class name and some text
    */

      const p1 = document.createElement('p');
      p1.className = 'notes-prev__title';
      p1.innerText = this.title;

      const p2 = document.createElement('p');
      p2.className = 'notes-prev__body';
      p2.innerText = this.body;

      // Nest p 1 & 2 inside 'div2'

      div2.appendChild(p1);
      div2.appendChild(p2);

      // Finally, render the div to its root tag

      notesDiv.appendChild(div1);
    }
  }

  /* 
    Every time this method is called, it creates 2 textareas, 
    one for the note title and the other for its body then it 
    appends it to the DOM.
  */

  renderNoteContent () {
    const title = document.createElement('textarea');
    title.placeholder = 'Title';
    title.value = this.title;
    title.className = 'note__title';
    
    const body = document.createElement('textarea');
    body.placeholder = 'Body';
    body.value = this.body;
    body.className = 'note__body';

    noteDiv.appendChild(title);
    noteDiv.appendChild(body);
  }

   /*
    When this method is called, it checks to see if there's a 
    note rendered already (childElementCount === 1 because there's a 
    button, so if there's only this button it means there's no 
    textareas rendered).
    
    If yes, then merely call the renderNoteContent method. Else
    get the tags with the classes 'note__title' and 'note__body'
    and remove them from the DOM, then call renderNoteContent to 
    create the textareas with the clicked notes values.

    This function gets mentioned at line 19.
  */

  clickHandler(thisClass) {
    if (noteDiv.childElementCount === 1) {
       thisClass.renderNoteContent();
    } else {
      document.querySelector('.note__title').remove();
      document.querySelector('.note__body').remove();
      thisClass.renderNoteContent();
    }
  }
}

Now i need 2 buttons, createNotesButton and saveNotesButton respectively. These 2 buttons must be inside a function that will be called inside .onAuthStateChanged (why? because they will be needing access to the currentUser on firebase auth).

I want the createNotesButton to create a note prototype, render it to the DOM and create a new document on firestore, where this note contents will be stored. Here's how i did it:

PS: I feel like i'm not using this class correctly, so again if you have any tips i appreciate it.

import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';

const htmlElements = [
  document.querySelector('.createNotes-button'), 
  document.querySelector('.saveNotes-button')
];

const [createNotesButton, saveNotesButton] = htmlElements;


function clickHandler(user) {
 
 /*

   1. Creates a class.

   2. Creates a new document on firebase with the class's empty value.

   3. Renders the empty class to the DOM.

 */

  createNotesButton.addEventListener('click', () => {

  const note = new CreateNote('', '');
  note.render();

 // Each user has it's own note collection, said collection has their `uid` as name.

    db.collection(`${user.uid}`).doc().set({
      title: `${note.title}`,
      body: `${note.body}`
    })
  })

}

Now i need a saveNotesButton , he's the one i'm having issues with. He needs to save the displayed note's content on firestore. Here's what i tried doing:

import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';

const htmlElements = [
  document.querySelector('.createNotes-button'), 
  document.querySelector('.saveNotes-button')
];

const [createNotesButton, saveNotesButton] = htmlElements;

function clickHandler(user) {
 
  createNotesButton.addEventListener('click', () => {...}) 

  /*

    1. Creates 2 variables, `title` and `body, if there's not a note being displayed
       their values will be null, which is why the rest of the code is inside an if
       statement

    2. If statement to check if there's a note being displayed, if yes then:
       
       1. Call the user's note collection. Any document who has the title field equal to the
          displayed note's value gets returned as a promise.

       2. Then call an specific user document and update the fields `title` and `body` with 
          the displayed note's values.

    3. If no then do nothing.

  */

  saveNotesButton.addEventListener('click', () => {

    const title = document.querySelector('.note__title');
    const body = document.querySelector('.note__body');


    db.collection(`${user.uid}`).where('title', '==', `${title.value}`)
      .get()
        .then(userCollection => {
          db.collection(`${user.uid}`).doc(`${userCollection.docs[0].id}`).update({
            title: `${title.value}`,
            body: `${body.value}`
          })
      })
      .catch(error => {
        console.log('Error getting documents: ', error);
      });
    });

}

This didn't work because i'm using title.value as a query, so if i change it's value it will also change the queries direction to a path that doesn't exist.

So here's the question: how can i make it so the saveNotesButton does its job? I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note. Again, if there's something in my code that you think can or should be formatted please let me know, i'm using this project as a way to solidify my native JS knowledge so please be patient. I feel like if i had used React i would've finished this sometime ago but definitely wouldn't have learned as much, anyway thanks for your help in advance.

I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note.

Yes, you absolutely need an immutable identifier for each note document in the firestore so you can unambiguously reference it. You almost always want this whenever you're storing a data object, in any application with any database.

But, the firestore already does this for you: after calling db.collection(user.uid).doc() you should get a doc with an ID . That's the ID you want to use when updating the note.

The part of your code that interacts with the DOM will need to keep track of this. I suggest moving the code the creates the firestore document into the constructor of CreateNote and storing it on this . You'll need the user id there as well.

  constructor(title, body, userId) {
     this.title = title;
     this.body = body;
     const docRef = db.collection(userId).doc();
     this.docId = docRef.id;

     /* etc. */

Then any time you have an instance of CreateNote , you'll know the right user and document to reference.

Other suggestions (since you asked)

  • Use JsPrettier. It's worth the setup, you'll never go back.
  • Use HTML semantics correctly. Divs shouldn't be appended as children of hrs, because they're for "a thematic break between paragraph-level elements: for example, a change of scene in a story, or a shift of topic within a section." MDN
  • For your next project, use a framework. Essentially no one hand-codes event listeners and appends children to get things done. I see the value for basic understanding, but there's a rich and beautiful world of frameworks out there; don't limit yourself by avoiding them:-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM