2

I am trying to build a website with a search field in which users can search for universities stored in an SQLite3 database. I hope to use Typeahead to give suggestions as the user types. However, I cannot get the Typeahead to work.

Here is the index.html file:

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Simple Sidebar - Start Bootstrap Template</title>

  <!-- Bootstrap core CSS -->
  <link href="static/bootstrap.min.css" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="static/simple-sidebar.css" rel="stylesheet">

  <!-- Use the external scripts -->
  <script src="/static/scripts.js"></script>
  <!-- http://jquery.com/ -->
  <script src="https://code.jquery.com/jquery-latest.min.js"></script>
  <!-- https://github.com/corejavascript/typeahead.js/blob/master/doc/jquery_typeahead.md -->
  <script src="https://cdn.rawgit.com/corejavascript/typeahead.js/master/dist/typeahead.jquery.js"></script>

  <!-- http://handlebarsjs.com/ -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.js"></script>

  <!-- https://developers.google.com/maps/documentation/javascript/ -->
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAgatq4KgsR2NHXXIG8YxiwncrsEz_xis8&v=3.exp"></script>

  <script>

    $(document).ready(function() {
      $('#q').typeahead({
        highlight: false,
        minLength: 1
      },
      {
          limit: 10,
          source: search,
            });
          });

    // Search database for typeahead's suggestions
    function search(query, syncResults, asyncResults) {
      // Get places matching query (asynchronously)
      let parameters = {
          q: query,
      };
      $.getJSON("/search", parameters, function(data, textStatus, jqXHR) {

          // Call typeahead's callback with search results (i.e., places)
          asyncResults(data);
      });
  }

  </script>

</head>

<body>

  <div class="d-flex" id="wrapper">

    <!-- Sidebar -->
    <div class="bg-light border-right" id="sidebar-wrapper">
      <div class="sidebar-heading">Start Bootstrap </div>
      <div class="list-group list-group-flush">
        <a href="#" class="list-group-item list-group-item-action bg-light">Dashboard</a>
        <a href="#" class="list-group-item list-group-item-action bg-light">Shortcuts</a>
        <a href="#" class="list-group-item list-group-item-action bg-light">Overview</a>
        <a href="#" class="list-group-item list-group-item-action bg-light">Events</a>
        <a href="#" class="list-group-item list-group-item-action bg-light">Profile</a>
        <a href="#" class="list-group-item list-group-item-action bg-light">Status</a>
      </div>
    </div>
    <!-- /#sidebar-wrapper -->

    <!-- Page Content -->
    <div id="page-content-wrapper">

      <nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
        <button class="btn btn-primary" id="menu-toggle">Toggle Menu</button>

        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav ml-auto mt-2 mt-lg-0">
            <li class="nav-item active">
              <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                Dropdown
              </a>
              <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                <a class="dropdown-item" href="#">Action</a>
                <a class="dropdown-item" href="#">Another action</a>
                <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="#">Something else here</a>
              </div>
            </li>
          </ul>
        </div>
      </nav>

      <div class="container-fluid">
        <h1 class="mt-4">Simple Sidebar</h1>
        <p>The starting state of the menu will appear collapsed on smaller screens, and will appear non-collapsed on larger screens. When toggled using the button below, the menu will change.</p>
        <p>Make sure to keep all page content within the <code>#page-content-wrapper</code>. The top navbar is optional, and just for demonstration. Just create an element with the <code>#menu-toggle</code> ID which will toggle the menu when clicked.</p>


        <div id="q">
          <input class="typeahead" type="text" placeholder="City (abbreviation), University Name, Website">
        </div>

      </div>
    </div>
    <!-- /#page-content-wrapper -->

  </div>
  <!-- /#wrapper -->

  <!-- Bootstrap core JavaScript -->
  <!-- <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
  -->

  <!-- Menu Toggle Script -->
  <script>
    $("#menu-toggle").click(function(e) {
      e.preventDefault();
      $("#wrapper").toggleClass("toggled");
    });
  </script>



</body>

</html>

And the application.py:

import os
from datetime import datetime
import copy

from cs50 import SQL
from flask import Flask, flash, redirect, render_template, request, session, jsonify
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions
from werkzeug.security import check_password_hash, generate_password_hash

# Configure application
app = Flask(__name__)

# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True

# Ensure responses aren't cached


@app.after_request
def after_request(response):
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response

# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

# Configure CS50 Library to use SQLite database
db = SQL("sqlite:///data.db")

@app.route("/", methods=["GET"])
def index():
    """Show portfolio of stocks"""
    #  which stocks the user owns, the numbers of shares owned, the current price of each stock, and the total value of each holding
    # [finds out the number of shares of each stock]select stock COUNT(shares) from transaction group by stock name having userid=current
    # Also display the user’s current cash balance along with a grand total (i.e., stocks' total value plus cash).

    return render_template("index.html")

@app.route("/search", methods=["GET"])
def search():
    """Search for places that match query"""

    # TODO
    if request.method == "GET":
        q = request.args.get("q") + "%"
        #q = request.args.get("q")
        row = db.execute(
            "SELECT * FROM universities WHERE city LIKE :q OR name LIKE :q OR website LIKE :q", q=q)
    return jsonify(row)

I have verified that the search() route works.

For the time being I included my javascript code inside the html instead of in a separate .js file because somehow the typeahead cannot start if I include it in the .js file and call it with the $(document).ready function.

Moreover, I tried using this typeahead syntax but it doesn't work either (the CS50 IDE showed an error "Handlebars was not defined; fix or add /* Global Handlebars*/", but I did not know how to fix it; I already included handlebar as a source script in the html head):

// configure typeahead
    $("#q").typeahead({
        // If true, when suggestions are rendered, pattern matches for the current query in text nodes
        // will be wrapped in a strong element with its class set to {{classNames.highlight}}. Defaults to false.
        highlight: false,
        // The minimum character length needed before suggestions start getting rendered. Defaults to 1.
        minLength: 1
    },
    {
        display: function(suggestion) { return null; },
        limit: 5,
        source: search,
        templates: {
            suggestion: Handlebars.compile(
                 "<div>" +
                "{{city}}, {{name}}, {{website}}" +
                "</div>"
            )
        }
    });

Any help would be highly appreciated. Thanks!

3
  • 1
    Any errors in the browser console? Suggestions only: Since you are basing this on CS50 mashup, suggest your index.html start out with the same <links> in the same order as pset8 distro. Is it dependencies? It's loading static/scripts.js before handlebars. Is the inline <script> redefining functions from the static/scripts? Start with what is known (i.e. all this works in cs50 mashup). – DinoCoderSaurus Mar 6 '19 at 12:18
  • Thank you @DinoCoderSaurus, I followed your suggestion, started with what's in cs50 mashup and it now works!! – Gladys Lau Mar 6 '19 at 13:52
  • Good news! I supplied the hint, you supplied the proof. Consider posting an answer (which you can "accept" after the wait period, like 2 days). This could be useful for others facing a similar problem. – DinoCoderSaurus Mar 6 '19 at 14:30
2

Eventually, I used the html and javascript files from cs50 mashup, and changed their content bit by bit (while mostly keeping the html head untouched). It worked, so I think the initial problem was related to missing parts from head and/or interference from the bootstrap html I adopted.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .