Search code examples
javascriptfirebasegoogle-cloud-firestoreweb-component

Refreshing data in a webcomponent from Firebase


UPDATE: I found that what I was looking for is Firebase's .onSnapshot function, as in:

db.collection("prjects")
    .onSnapshot(function(data) {}

My current problem is that I cannot pass a reference into .onSnapshot(). Here's what I mean:

import Template from "./template.js";
import { db } from "../../util/firebase.js";

export default class Dashboard extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({ mode: "open" });

        this.shadowRoot.innerHTML = Template.render();
        this.dom = Template.mapDOM(this.shadowRoot);

        if (this.dom.projectList) {
            console.log("this.dom.projectList is defined")
        } // checking if this.dom.projectList is defined: it is!

        db.collection("projects")
            .onSnapshot(function (data) {
                var html = "";
                data.forEach(function (doc) {
                    const project = doc.data();
                    const li = `
                        <li>
                            <h5>${project.title}</h5>
                            <p>${project.description}</p>
                        </li>
                    `;
                    html += li;
                    console.log("test", html);
                });

                this.dom.projectList.innerHTML = html;  // not defined!!

            }, error => { console.log("Oops! ", error) });
    }

I still can't refresh the page content... How can I get access to "this.dom.projectList" inside .onSnpashot() ?

=======================================================================

I am developping a webcomponent that lists projects stored in a Firebase Firestore database and I want it to auto-refresh when the someone adds another project in Firestore.

My problem is with the second part (refreshing). My understanding of webcomponents life-cycle is that potential useful states are: constructor (component is loaded in memory, happen once), the connectedCallback (component displayed), attributeChangedCallback (attribute has changed, state changed). This last one might be interesting but passing tens of attrbutes in an element, sounds strange to me: firebase's "snapshot.docs" can contain a lot of data to deserialize.

I am trying to see new content without reloading the whole page.

How do you solve this problem?

Here is my current web-component code:

import Template from "./template.js";
import {db} from "../../util/firebase.js";

export default class Dashboard extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({mode:"open"});

        db.collection("projects").get().then(snapshot => {
            this.setupProjects(snapshot.docs);
            const data = snapshot.docs;
            data.forEach(doc => {
                console.log(doc.data().title)
            })
        }); 
    
    


        this.shadowRoot.innerHTML = Template.render();
        this.dom = Template.mapDOM(this.shadowRoot);
    }

    setupProjects(data) {
        let html = "";
        data.forEach(doc => {
            const project = doc.data();
            const li = `
                <li>
                    <h5>${project.title}</h5>
                    <p>${project.description}</p>
                </li>
            `;
            html += li;
        })

        this.dom.projectList.innerHTML = html;
    }
};

if (!customElements.get("dashboard-page")){
    customElements.define("dashboard-page", Dashboard);
}

And this is how my template is built:

export default {
    render() {
        return `
        ${this.html()}
        ${this.css()}`
    },
    mapDOM(scope){
        return {
            projectList: scope.querySelector("#projectList")
        }
    },
    html() {
        return `
            Welcome Home, logged in.
            <ul id="projectList"></ul>
            `
    },
    css(){
        return`
        <style>
        </style>`
    }
}

```

Any idea will be welcome because I have none...

Thank you!

Solution

  • So far, my understanding is that it was a scope issue. I added:

    let that = this;
    

    before:

     db.collection("projects").get().then(snapshot => {
            that.setupProjects(snapshot.docs);  // notice the "that", not this.
            const data = snapshot.docs;
            data.forEach(doc => {
                console.log(doc.data().title)
            })
        }
     ); 
    

    Now it works!