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!
<links>
in the same order as pset8 distro. Is it dependencies? It's loadingstatic/scripts.js
beforehandlebars
. Is the inline<script>
redefining functions from the static/scripts? Start with what is known (i.e. all this works in cs50 mashup).