dna.js Semantic Templates

An uncomplicated user interface library

Fork me on GitHub

REST-driven Search Component Tutorial

Follow the steps below to build an interactive book search tool on top of the Google Books API.

google View a sample of the JSON results you will be using: https://www.googleapis.com/books/v1/volumes?q=ice

Steps:

  1. Create baseline HTML
  2. Add search component
  3. Add search results component
  4. Style search results
  5. Define book template
  6. Load jQuery and dna.js
  7. Wire up search callback
  8. Make REST call
  9. Smarten up the search field
  10. Conclusion
  11. Questions and comments

Step 1: Create baseline HTML

Using your favorite code editor, create an HTML file named book-finder.html with the content below.

Initial contents for book-finder.html

            <!doctype html>
            <html>
            <head>
            <meta charset=utf-8>
            <title>Book Finder</title>
            </head>
            <body>

            <main>
               <h1>Book Finder</h1>
            </main>

            </body>
            </html>
         

Open book-finder.html in your web browser to verify it displays properly.

Step 2: Add search component

Just after the h1 header line, insert the HTML for the search input box and the submit button.

Example HTML

            <main>
               <h1>Book Finder</h1>
               <label>
                  Search:
                  <input placeholder="Enter terms" autofocus>
               </label>
               <button>Find</button>
            </main>
         

Now we need some HTML to display the search results.

Step 3: Add search results component

Just after the search component you added in the previous step, insert a div tag to hold a list of books.  Then add the static HTML for a single book.

HTML for books

            <div class=books>
               <div class=book>
                  <img src=http://dnajs.org/graphics/sample-book-cover.jpg alt=cover>
                  <div>
                     <b>Title</b>
                     <p>Publisher</p>
                     <i>Price</i>
                  </div>
               </div>
            </div>
         

If you go to your browser and refresh the book-finder.html page, you'll see the sample book desperately needs some styling.

Step 4: Style search results

Add the following lines into the <head> section:

Example HTML

            <style>
               body       { font-family: sans-serif; margin: 30px; }
               .book      { display: table; background-color: lightblue;
                            padding: 10px; margin: 10px; }
               .book img  { width: 80px; margin-right: 10px; }
               .book >div { display: inline-block; width: 400px;
                            vertical-align: top; }
            </style>
         

The book-finder.html page looks better with a little CSS, but it's still completely static.  Next we'll turn the static book HTML into a template that can take JSON data.

Step 5: Define book template

Before we convert the static book HTML into a data-driven template, we need to know the structure of the data.  Do this by manually examining the results of a sample search.

Below are the pertinent fields:

Sample Google Books API response

            {
               "totalItems": 1026,
               "items": [
                  {
                     "id": "YfimjGQIo7gC",
                     "volumeInfo": {
                        "title": "The Book of Ice",
                        "publisher": "Subliminal Kid Inc",
                        "imageLinks": {
                           "thumbnail": "http://books.google.com/books/..."
                        }
                     },
                     "saleInfo": {
                        "listPrice": {
                           "amount": 9.99
                        }
                     }
                  }
               ]
            }
         

The API returns a single object.  The data we want is the list of books, which is the items array.

The desired fields from each book are:

Convert the book HTML into a template by changing the class book to an ID (name of the template) and adding the class dna-template.  Also insert the data fields surrounded by double tildes (~~) into the desired spots within the template.

Book template

            <div id=book class=dna-template>
               <img src=~~volumeInfo.imageLinks.thumbnail~~ alt=cover>
               <div>
                  <b>~~volumeInfo.title~~</b>
                  <p>~~volumeInfo.publisher~~</p>
                  <i>~~saleInfo.listPrice.amount~~</i>
               </div>
            </div>
         

Note: For a production website, set the image src attribute to # and specify the data field in the data-attr-src attribute.  This apparoch maintains valid HTML.

Now it's time to load some libraries that will help us build dynamic features.

Step 6: Load jQuery and dna.js

The easiest way to get the necessary CSS and JavaScript libraries is the pull them from a CDN.

First, add the following line into the <head> section:

CSS link in head section

            <link rel=stylesheet href=https://cdn.jsdelivr.net/dna.js/1.2/dna.css>
         

Then, add the following two lines just before the closing </body> tag:

JS link at bottom of the body section

            <script src=https://cdn.jsdelivr.net/jquery/3.1/jquery.min.js></script>
            <script src=https://cdn.jsdelivr.net/dna.js/1.2/dna.min.js></script>
         

Go to your web browser and reload the book-finder.html page.  Verify jQuery and dna.js loaded properly by going into the JavaScript console and entering the command: dna.info();

Step 7: Wire up search callback

Start by writing the shell of a JavaScript function called findBooks().

Put the function just before the closing </body> tag:

Callback event for button

            <script>
               function findBooks(elem) {
                  var terms = $('input').val();
                  console.log(terms);
                  }
            </script>
         

To wire up the funciton to the "Find" button, we'll use the dna-click attribute.  The attribute tells dna.js which function to call when the user clicks the element.

Set the data-click attribute to findBooks on the search component button:

Callback event for button

            <button data-click=findBooks>Find</button>
         

Verify the click events are firing the callback by viewing the JavaScript console while clicking the "Find" button.

Step 8: Make REST call

Immediately after the console.log line, insert the following code:

REST

            var url = 'https://www.googleapis.com/books/v1/volumes?q=' + terms;
            function handleResults(data) {
               dna.clone('book', data.items, { empty: true, fade: true });
               }
            $.getJSON(url, handleResults);
         
The code:
  1. Builds the REST URL
  2. Invokes jQuery's getJSON() function to call the Google Books API
  3. Passes the search results data to the templating engine.

The options { empty: true, fade: true } tell dna.js to delete any previous results before displaying the new results and to smoothly fade in the new results.

Go to your web browser and give it a whirl by searching for "laser".

Step 9: Smarten up the search field

Clicking the "Find" button may be simple, but it makes for a cumbersome user experience.  We can leverage the smart update feature in dna.js to replace the button with automatic searching.

Delete the line of HTML for the <button>, and update the <input> tag to be:

Smart update

            <input data-smart-update=findBooks placeholder="Enter terms" autofocus>
         

dna.js always passes the event target element into the callback.  The callback event now happens on the <input> element, so we can replace the old var terms = $('input').val(); line with a more robust line of code:

Smart update

            var terms = elem.val();
         

Hop back to your browser and verify that search results are returned continuously as you type in search terms.

Conclusion

That's a wrap!

Finished version:

Code:

book-finder.html

            <!doctype html>
            <html>
            <head>
            <meta charset=utf-8>
            <title>Book Finder</title>
            <link rel=stylesheet href=https://cdn.jsdelivr.net/dna.js/1.2/dna.css>
            <style>
               body       { font-family: sans-serif; margin: 30px; }
               .book      { display: table; background-color: lightblue;
                            padding: 10px; margin: 10px; }
               .book img  { width: 80px; margin-right: 10px; }
               .book >div { display: inline-block; width: 400px;
                            vertical-align: top; }
            </style>
            </head>
            <body>

            <main>
               <h1>Book Finder</h1>
               <label>
                  Search:
                  <input data-smart-update=findBooks placeholder="Enter terms" autofocus>
               </label>
               <div class=books>
                  <div id=book class=dna-template>
                     <img src=~~volumeInfo.imageLinks.thumbnail~~ alt=cover>
                     <div>
                        <b>~~volumeInfo.title~~</b>
                        <p>~~volumeInfo.publisher~~</p>
                        <i>~~saleInfo.listPrice.amount~~</i>
                     </div>
                  </div>
               </div>
            </main>

            <script src=https://cdn.jsdelivr.net/jquery/3.1/jquery.min.js></script>
            <script src=https://cdn.jsdelivr.net/dna.js/1.2/dna.min.js></script>
            <script>
               function findBooks(elem) {
                  var terms = elem.val();
                  console.log(terms);
                  var url = 'https://www.googleapis.com/books/v1/volumes?q=' + terms;
                  function handleResults(data) {
                     dna.clone('book', data.items, { empty: true, fade: true });
                     }
                  $.getJSON(url, handleResults);
                  }
            </script>
            </body>
            </html>
         

Hungry for more?  Try building something using the Google Maps APIs Web Services.

For example, here's the JSON data for the geographic coordinates of Paris: maps.googleapis.com/maps/api/geocode/json?address=Paris

Questions and comments

Tweet your question or comment with or post below.