Skip to content

Latest commit

 

History

History
585 lines (466 loc) · 23.7 KB

LabGuide300.md

File metadata and controls

585 lines (466 loc) · 23.7 KB


Updated: November 6, 2018

Introduction

Now we will try connecting to a non-Oracle Cloud Database; in this case, Google's Firebase.

To log issues, click here to go to the github oracle repository issue submission form.

Objectives

  • Learn how to set up a Firebase Database
  • Connect to VBCS
    • Display information from Database
    • Request specific data

Required Artifacts

  • Cloud trial account.

Lab 300

Connecting to Firebase Database

STEP 1: Set Up Database

Create Firebase Account

Visit Firebase's website. On the top right corner, click Sign in.



Your Firebase account is automatically linked with your google account, so you can sign in with your gmail account credentials to get started. If you don't have one, sign up for one and then revisit the Firebase website. Once logged in, click Get Started.



You are now on the main dashboard page and can move on to the next section.



Create Realtime Database

Click "Add Project". Choose a name, leave the default settings for location, make sure all three boxes are checked, then hit Create Project.



It will take 10 seconds or so to create, then the page should redirect you to your Database home page. Note that currently, there is no data in our database.


First thing we need to do is edit the security rules to allow read write access. Since this is just a test database, it doesn't need to be secure. Go to the Rules tab and simply change read and write to "true". For a real project, you would want more specific rules. Google has documentation on how to create more complex rules here.

Populate Database

Now, inside this GitHub repository, navigate to the "resources" directory and download the bookList.json file. Open it inside VCode or your preferred text editor. Note the structure is of several book objects identified by ISBN.


Go back to the Data tab of your Database. Near the top right, hit the three dots dropdown, then "Import JSON".


Import the bookList.json file.


Your database should populate with the information from the file.


To test that everything is set up correctly, enter the shown url for the Database /books.json into a browser with /books.json appended to the end of the URL.

https://projectname-XXXXX.firebaseio.com/books.json

A list of the books and all their info should be shown.


Side Note: If the formatting of your data looks different, add the JSON Viewer extension to your Chrome browser: https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh?hl=en-US.

STEP 2: Loading Book Data on VBCS

Create New Page

The first thing we want to do is create another page on which we'll display our book descriptions/images. Let's call this page book-catalog. To make this page, right click on main-start and hit Duplicate. Then, right click on the copy page to rename it book-catalog. On the Design view of the page, click on the "Welcome to the Home Page" heading, then hit the trash can icon in the bottom left corner of the right-side bar to delete the component. Your page should look like:




Now, we have to update our tab bar to include this new page we just made. Go to the code view for the page, and look for the "oj-tab-bar-XXXXXXXXX-X" item. Inside that, you should see two oj-tab-bar-XXXXXXXXX-X-tab-X items. Copy the code for the first tab (the one with dull formatting) and paste it right below the code for the second tab. Rename the tab Catalog and change the listener to clickCatalogTab (this event doesn't exist yet, but we'll make it soon). Finally, change the first tab's style to bright, so only the third tab is dull.



Note: You could have gone to customize the tab bar on the Design view and hit the plus sign to the right of the title Tabs in the customization bar, but this would not have copied the style or the listener. For this reason, we decided to manually copy the code in ourselves.


Repeat this process for the other pages. On the other pages, the Catalog tab should have bright styling.



Now we just need to create our action chain navigateCatalogPage (created at the flow level) and our event clickCatalogTab (created for each page) and we are good to go. Double check that you can navigate between all three pages.

Add HTML/CSS

Now that our database has been set up, we'll need to connect it to VBCS. We'll be using this database information to populate one of our pages with images and descriptions of books, so the first thing we need to do is to come up with a layout of how we want our page to look. For this lab, we'll format the page with a left-side column to display book images and a right-side column to display the book information.

Let's create this layout by adding the HTML structure to our new book-catalog page. Navigate to the Code view of the page, and copy and paste this HTML code and add it at the very end:

<div class="row">
   <div class="column"> <div id="leftColumn"></div> </div>
   <div class="column"> <div id="rightColumn"></div> </div>
</div>

With the HTML in place, we can next add the css for the two columns to style them properly:

  .column {
    float: left;
    width: 35%;
}

.row:after {
    content: "";
    display: table;
    clear: both;
}



With these 2 div objects properly set up, we'll be able to identify where the javascript should populate the images and descriptions. Let's move on to the actual function.

Create Javascript Function
VBCS requires that functions be written in a very particular way. You will see the base outline for this already here.



The outermost function will return a PageModule object to VBCS; it sends all of the module functions we create to VBCS so we can more easily access them in other components. Each module can be treated like a separate Javascript file.

To define a module, use this format:

PageModule.prototype.functionName = function () { OUR CODE HERE };

Our functions will look like:

define([], function() {
  'use strict';

  var PageModule = function PageModule() {};
  PageModule.prototype.functionOne = function () { OUR CODE HERE };
  PageModule.prototype.functionTwo = function () { OUR CODE HERE };
  PageModule.prototype.functionThree = function () { OUR CODE HERE };

  return PageModule;
});

To get started, let's set up the module that will load the book descriptions.

define([], function() {
  'use strict';

  var PageModule = function PageModule() {};
  
  PageModule.prototype.loadDescriptions = function () {
    //code here
  };

  return PageModule;
});

Then we want to grab the rightcolumn so that we can append elements to that part of the page. Put this in the "code here" section.

const app = document.getElementById('rightColumn');

Now we are ready to make our GET request to our database. Make sure to replace the url below with the url for your database.

var request = new XMLHttpRequest();
request.open('GET', 'https://projectname-XXXXXX.firebaseio.com/books.json', true);

We want to peform some actions once this request is made.

request.onload = function () {
  //actions to perform once request is made
}

Before we can do anything with the response, we have to parse it as a JSON. Put this code inside the request.onload function.

var data = JSON.parse(this.response);

Right below that, put this code. It will run desired actions if the request is a success, and return an error if there's a problem.

if (request.status >= 200 && request.status < 400) {
  //actions to perform on successful request
}
else {
  const errorMessage = document.createElement('marquee');
  errorMessage.textContent = "Request failed.";
  app.appendChild(errorMessage);
}

Next we are going to run through the children of the JSON response and add each entry as a line on our webpage. We'll also add a horizontal rule between each, and use a blank image to add some space between book descriptions.

      if (request.status >= 200 && request.status < 400) {
        Object.keys(data).forEach(result => {
          const line = document.createElement('hr');
          app.appendChild(line);
            
          const title = document.createElement('p');
          title.textContent = data[result].title;
          app.appendChild(title);
          const author = document.createElement('p');
          author.textContent = data[result].author;
          app.appendChild(author);
          const ISBN = document.createElement('p');
          ISBN.textContent = result;
          app.appendChild(ISBN);
          const genre = document.createElement('p');
          genre.textContent = data[result].genre;
          app.appendChild(genre);
          const published = document.createElement('p');
          published.textContent = data[result].publish_date;
          app.appendChild(published);
          const publisher = document.createElement('p');
          publisher.textContent = data[result].publisher;
          app.appendChild(publisher);
            
          const space = document.createElement('img');
          space.src = "https://i.imgur.com/gAYM6Ws.png?3";
          app.appendChild(space);
        });
      }

Finally, all together:

PageModule.prototype.loadDescriptions = function () {
            
      const app = document.getElementById('rightColumn');      

      var request = new XMLHttpRequest();
      request.open('GET', 'https://asset-bdf37.firebaseio.com/results.json', true);

      request.onload = function () {
        // Begin accessing JSON data here
        var data = JSON.parse(this.response);
        if (request.status >= 200 && request.status < 400) {
          Object.keys(data).forEach(result => {
            const line = document.createElement('hr');
            app.appendChild(line);
            
            const title = document.createElement('p');
            title.textContent = data[result].title;
            app.appendChild(title);
            const author = document.createElement('p');
            author.textContent = data[result].author;
            app.appendChild(author);
            const ISBN = document.createElement('p');
            ISBN.textContent = result;
            app.appendChild(ISBN);
            const genre = document.createElement('p');
            genre.textContent = data[result].genre;
            app.appendChild(genre);
            const published = document.createElement('p');
            published.textContent = data[result].publish_date;
            app.appendChild(published);
            const publisher = document.createElement('p');
            publisher.textContent = data[result].publisher;
            app.appendChild(publisher);
            
            const space = document.createElement('img');
            space.src = "https://i.imgur.com/gAYM6Ws.png?3";
            app.appendChild(space);
          });
        }
        else {
          const errorMessage = document.createElement('marquee');
          errorMessage.textContent = "Request failed.";
          app.appendChild(errorMessage);
        }
      }

      request.send();
    };

Careful with your brackets here; it's easy to get one too many or one too few.

Calling Function on Page

We want this function to be called whenever the page loads. Go to Events on the left sidebar for the Catalog page.
Click Create Event Listener, then under Lifecycle Events, select vbEnter. This will be an event that runs whenever the page loads.


Hit the + sign next to Page Action Chains to create a new action chain. Name this runLoadDescriptions.


Click on the name of your event, then on the right side hit the link to open the action chain editor.


Drag "Call Module Function" onto the plus sign.


Select Module Function. You should see a Page Function named loadDescriptions in the list. Select it, and you should be good to go.


Test the page, and the books should appear on the Catalog page.


Now we are going to create our other module, loadImages. The process is basically the same, except we are appending images instead of text.
Insert this code alongside the first module:

  PageModule.prototype.loadImages = function() {
    const app = document.getElementById('leftColumn');

    var request = new XMLHttpRequest();
    request.open('GET', 'https://asset-bdf37.firebaseio.com/results.json', true);

    request.onload = function () {
      // Begin accessing JSON data here
      var data = JSON.parse(this.response);
      if (request.status >= 200 && request.status < 400) {
        Object.keys(data).forEach(result => {
          const bookCovers = document.createElement('img');
          bookCovers.src = data[result].image_url;
          console.log(result);
          app.appendChild(bookCovers);
          const p = document.createElement('p');
          p.textContent = "\n";
          app.appendChild(p);
        });
      }
      else {
        const errorMessage = document.createElement('marquee');
        errorMessage.textContent = "Request failed.";
        app.appendChild(errorMessage);
      }
    }

    request.send();
  };

Add another action change under vbEnter, this one called runLoadImages. Set it up the same as runLoadDescriptions, with this one calling the loadImages module.

Test the page one more time, and we should see the book covers to the left of the book descriptions.


Great job!

STEP 2: Searching Books on VBCS

Set Up Search Page

Let's review what we've done until this point. So far, we've built our web application, created a Firebase database and populated it with information, and wrote custom Javascript to extract data from our database URL. We invoked those functions and had them run at page load time, and we were able to display book images and descriptions on our catalog page. Great! But what if we want to display books based on a user search? That takes a bit of extra work. We'll need to first capture the user's input, and then parse our JSON object accordingly.

First create a third page for this website's search functionality. We'll call it "search". Duplicate main-start and rename the copy search.
Change "Welcome to the Home page." to say "Search". Drag and drop a user input box for the user to type in their search term, followed by a button for running that search. Click on the Input Text label and change it to say "Genre:". Let's also drag over a button to the right of the input text. Change the text of the button to "search".



Note, however, that we only have three tabs; we need to make one more tab for the new page.
Briefly,
-Copy and paste code for a new tab in each page.
-Change the tab name to "Search" and the onclick listener to clickSearchTab.
-Create an action chain navigateSearchPage at the flow level.
-Create an event listener on each page called clickSearchTab.
Review Step 2. if you want more specific instructions.



Now that we've finished our simple layout, we need to save the user's input into a variable. On the left side click the (x) icon to open up Variables page. Create a new variable and call it "genre".



Go back to the search page and click on the text input box. Under Data, enter {{ $page.variables.genre }}. This saves the value that the user types into our genre variable.



Create Search Function

Next, let's copy over the Javascript code. Under the JS tab of our catalog page, copy and paste the two slightly modified functions below onto our search page.

  PageModule.prototype.loadDescriptions = function (inputGenre) { // our function now takes in a "genre" input

       const app = document.getElementById('rightColumn');      

       var request = new XMLHttpRequest();
       request.open('GET', 'https://asset-bdf37.firebaseio.com/results.json', true);

       request.onload = function () {
         // Begin accessing JSON data here
         var data = JSON.parse(this.response);
         if (request.status >= 200 && request.status < 400) {
           Object.keys(data).forEach(result => {     
             if(data[result].genre == inputGenre){ // we'll only want to display descriptions for a specific genre
               const line = document.createElement('hr');
               app.appendChild(line);

               const title = document.createElement('p');
               title.textContent = data[result].title;
               app.appendChild(title);
               const author = document.createElement('p');
               author.textContent = data[result].author;
               app.appendChild(author);
               const ISBN = document.createElement('p');
               ISBN.textContent = result;
               app.appendChild(ISBN);
               const genre = document.createElement('p');
               genre.textContent = data[result].genre;
               app.appendChild(genre);
               const published = document.createElement('p');
               published.textContent = data[result].publish_date;
               app.appendChild(published);
               const publisher = document.createElement('p');
               publisher.textContent = data[result].publisher;
               app.appendChild(publisher);

               const space = document.createElement('img');
               space.src = "https://i.imgur.com/gAYM6Ws.png?3";
               app.appendChild(space);
             }
           });
         }
         else {
           const errorMessage = document.createElement('marquee');
           errorMessage.textContent = "Request failed.";
           app.appendChild(errorMessage);
         }
       }
       request.send();
     }; 
  PageModule.prototype.loadImages = function(inputGenre) { // our function now takes in a "genre" input
       const app = document.getElementById('leftColumn');

       var request = new XMLHttpRequest();
       request.open('GET', 'https://asset-bdf37.firebaseio.com/results.json', true);

       request.onload = function () {
         // Begin accessing JSON data here
         var data = JSON.parse(this.response);
         if (request.status >= 200 && request.status < 400) {
           Object.keys(data).forEach(result => {
             if(data[result].genre == inputGenre){ // we'll only want to display images for a specific genre
               const bookCovers = document.createElement('img');
               bookCovers.src = data[result].image_url;
               console.log(result);
               app.appendChild(bookCovers);
               const p = document.createElement('p');
               p.textContent = "\n";
               app.appendChild(p);
             }
           });
         }
         else {
           const errorMessage = document.createElement('marquee');
           errorMessage.textContent = "Request failed.";
           app.appendChild(errorMessage);
         }
       }

       request.send();
     };



Call Search Function

Now that we have our logic, let's bind this logic to an action. Under Designer view, click the Search button. Under the Events tab, click New Event -> Quick Start Click.



An action chain window has popped up. Drag over a Call Module Function. Click Select Module Function. Under "Page Functions", select our loadImages function.



Recall that our function now takes in a paramter, so on the right side under Input Paramters, map inputGenre to our Genre variable. Click save.



Now perform the same steps for the loadDescriptions function (drag another module function in for the loadDescriptions function, and bind the paramters to the function). The end action chain should look like this:



Let's test our page out. Click the Live button at the top right corner. Enter in Fantasy and hit search. Our website now loads all the books with the fantasy genre! (If the search button displays at the bottom of the page instead of the top, re-order the left-column and right-column HTML divs to the end of your page HTML code).



Try hitting the search button again. Uh oh, looks like the page is getting populated with the same books every time someone hits search.



We'll fix this by first removing the book images/descriptions every time someone hits search before loading the new images/descriptions.

Go to the js tab, and paste in the following function that will clear the book images/descriptions:

 PageModule.prototype.resetPage = function () {
     const col1 = document.getElementById('leftColumn');
     const col2 = document.getElementById('rightColumn');
     while (col1.firstChild) { // while there are images, remove them
       col1.removeChild(col1.firstChild);
     }
     while (col2.firstChild) { // while there are descriptions, remove them
       col2.removeChild(col2.firstChild);
     }
   };



With this new function added, navigate to our action chain that invokes the loadImage and loadDescription functions. Add a new module function that calls on the resetPage function.







Now go back to the Designer view, click the submit button, and bind this action chain to whenever someone clicks the search button. There are now three actions within this action chain. One to remove any previous search results, one to load descriptions, and the last to load images.



Try loading the page again. It works! We have now successfully implemented the search functionality.