Wiring Up A Firestore Database to jQuery App
Jquery 31-Jul-2018

Wiring Up A Firestore Database to jQuery App

This is a quick example to see how to wire up a Firestore database to a simple jQuery app that will demonstrate basic CRUD operations (create/read/update/delete).

1. Setup Firestore Database

  • Go to Firebase console and add new project
  • On the side menu: Develop > Database > Cloud Firestore > Create Database. Select Start in test mode to allow all reads and writes to your database.
  • Add a new collection called "contacts"
  • Enable Anonymous authentication at Authentication > SIGN-IN METHOD > Anonymous > Enable > SAVE.
  • Go to Project Overview > click "Add Firebase to your web app" and copy initialization config and place it in this example Javascript.
  • For more info, check Firebase Web Setup.

 

Setup Firestore Database 1 Setup Firestore Database 2
Setup Firestore Database 3 Setup Firestore Database 4

 

2. HTML5 Code

Now will create a new HTML page that includes Firebase javascript library, jQuery and Bootstrap (for quick styling).


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Contacts</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <!-- Firebase -->
    <script src="https://www.gstatic.com/firebasejs/5.2.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.2.0/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.2.0/firebase-firestore.js"></script>
    <script>
        // Initialize Firebase : Replace with your Project Config!!!!!!
        var config = {
            apiKey: "?",
            authDomain: "?",
            databaseURL: "?",
            projectId: "?",
            storageBucket: "?",
            messagingSenderId: "?"
        };
        //////////////////////////////////////////////////////////////
        firebase.initializeApp(config);
    
    </script>

    <!-- Bootstrap/jQuery JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    <!-- Contacts JS-->
    <script src="index.js"></script>
</head>
<body>

    <!-- Contacts Table -->
    <table id="ContactTable" class="table table-striped">
     <thead>
         <tr>
             <th>Name</th>
             <th>Email</th>
             <th>Phone</th>
             <td>...</td>
         </tr>
     </thead>
     <tbody>
         <tr class="data-temp">
             <td data-prop="name"></td>
             <td data-prop="email"></td>
             <td data-prop="phone"></td>
             <td>
                 <button class="btn btn-secondary edit">edit</button>
                 <button class="btn btn-danger remove">remove</button>
             </td>
         </tr>
     </tbody>
     <tfoot>
         <tr>
             <td colspan="3"></td>
             <td><button class="btn btn-dark add" id="ContactAdd">add</button></td>
         </tr>
     </tfoot>
    </table>

    <!-- Contact Form -->
    <form id="ContactForm">
        <div class="modal fade" id="ContactModal" tabindex="-1" role="dialog" aria-hidden="true">
            <div class="modal-dialog" role="document">
    <div class="modal-content">
        <div class="modal-header">
            <h5 class="modal-title"></h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        </div>
        <div class="modal-body">
            <div class="form-group">
                <label>Name</label>
         <input type="text" class="form-control" data-prop="name" placeholder="Enter name" maxlength="100" required />
            </div>
            <div class="form-group">
                <label>Email</label>
         <input type="email" class="form-control" data-prop="email" placeholder="Enter email" maxlength="100" required />
            </div>
            <div class="form-group">
                <label>Phone</label>
         <input type="tel" pattern="^[\d\-+]*$" class="form-control" data-prop="phone" placeholder="Enter phone" maxlength="50" />
            </div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="submit" class="btn btn-primary">Save</button>
        </div>
    </div>
            </div>
        </div>
    </form>

</body>
</html>



Note it is only including the components we need : firebase-app, firebase-auth and firebase-firestore.
Note the "data-prop" attribute with values matching the contact fields in table template row and also in the form inputs.
 

3. Javascript Code

This is the jQuery code saved in "index.js"

(function ($) {
    // firestore ref
    var db;

    // auth and setup event handlers
    var init = function () {
        auth();

        $('#ContactTable').on('click', 'button.edit', edit);
        $('#ContactTable').on('click', 'button.remove', remove);
        $('#ContactAdd').click(add);
        $('#ContactForm').submit(save);
    };

    // init on doc ready
    $(document).ready(init);

    // sign-in anonymously
    var auth = function () {
        firebase.auth().signInAnonymously()
        .then(function (result) {
     db = firebase.firestore();
     db.settings({ timestampsInSnapshots: true });

     list();
        })
        .catch(function (error) {
     alert("failed to anonymously sign-in");
        });
    };


    var listTempTr;
    // load list
    var list = function () {
        var tblBody = $('#ContactTable > tbody');
        //remove any data rows
        tblBody.find('tr.data').remove();
        //get template row
        var tempTr = tblBody.find('tr.data-temp').removeClass('data-temp').addClass('data').remove();
        if (tempTr.length) {
            listTempTr = tempTr;
        } else {
            tempTr = listTempTr;
        }

        // get collection of Contacts
        db.collection("contacts").get().then(function (querySnapshot) {
            querySnapshot.forEach(function (doc) {
                // clone template row and append to table body
                var tr = tempTr.clone();
                tr.data('id', doc.id);
                var data = doc.data();
                // set cell values from Contact data
                tr.find('td[data-prop]').each(function () {
                    var td = $(this);
                    td.text(data[td.data('prop')] || '');
                });
                tblBody.append(tr);
            });
        });
    };

    // on remove
    var remove = function (e) {
        e.preventDefault();
        var id = $(this).parents('tr:first').data('id');
        db.collection("contacts").doc(id).delete().then(function () {
            // reload list
            list();
        })
        .catch(function (error) {
     alert("failed to remove contact");
        });
    };

    // on add
    var add = function (e) {
        e.preventDefault();
        open('');
    };

    // on edit
    var edit = function (e) {
        e.preventDefault();
        var id = $(this).parents('tr:first').data('id');
        open(id);
    };

    // open form modal
    var open = function (id) {
        var modal = $('#ContactModal');
        // set current Contact id
        modal.data('id', id);
        // reset all inputs
        modal.find('input').val('');
        modal.modal('show');

        if (!id) return;

        // get Contact to edit
        db.collection("contacts").doc(id).get().then(function (doc) {
            if (doc.exists) {
                var data = doc.data();
                //set form inputs from Contact data
                modal.find('input[data-prop]').each(function () {
                    var inp = $(this);
                    inp.val(data[inp.data('prop')] || '');
                });

            } else {
                alert("No such record");
            }
        }).catch(function (error) {
            alert("failed to read contact");
        });
    };

    // update or add
    var save = function (e) {
        e.preventDefault();

        var modal = $('#ContactModal');
        var id = modal.data('id');
        var data = {};
        //read values from form inputs
        modal.find('input[data-prop]').each(function () {
            var inp = $(this);
            data[inp.data('prop')] = inp.val();
        });

        // update or add
        (id ? db.collection("contacts").doc(id).update(data) : db.collection("contacts").add(data)).then(function (result) {
            // hide modal and reload list
            modal.modal('hide');
            list();
        })
        .catch(function (error) {
     alert("failed to save contact");
        });
    };

}(jQuery));


First -on Document Ready- it will anonymously sign-in user. Also attaching event handlers for the edit/remove/add buttons and form submisson.
Then get the contacts collection, and loop each document to add a new row in the contacts table.
On Add/Edit, it will open a Bootstrap modal to display the form. and will load existing Contact data to edit.
On Save, will decide to use update() or add() based on contact ID.

Now, save the HTML and Javascript files and give it a try.