0

I'm currently working on my final project, a website. I'm trying to include a search suggestion box (typeahead), similar to that of pset8's. I tried to implement it similar to pset8's, but for some reason, it is not working. Can someone please help me locate the problem in my code, or point out something that I am missing to make it complete?

Here's the python function (it's the index function but it has the search bar):

@app.route("/")
@login_required
def index():
    if request.method == "POST":
        q = request.form.get("search") + "%"
        set_results = db.execute("SELECT * from all_sets WHERE Name LIKE :q OR Number LIKE :q OR Year LIKE :q OR Subtheme LIKE :q OR Pieces LIKE :q OR USPrice LIKE :q", q=q)
        return jsonify(set_results)
    else:
        return render_template("index.html") 

And here's the js:

function search(query, syncResults, asyncResults)
{
    // get places matching query (asynchronously)
    var parameters = {
        q: query
    };
    $.getJSON(Flask.url_for("index"), parameters)
    .done(function(data, textStatus, jqXHR) {
     
        // call typeahead's callback with search results (i.e., places)
        asyncResults(data);
    })
    .fail(function(jqXHR, textStatus, errorThrown) {

        // log error to browser's console
        console.log(errorThrown.toString());

        // call typeahead's callback with no results
        asyncResults([]);
    });
}
$(function() {
    $("#search").typeahead({
        highlight: true,
        minLength: 1
    },
    {
        display: function(suggestion) { return null; },
        limit: 10,
        source: search,
        templates: {
            suggestion: Handlebars.compile(
                "<div>" +
                "{{Name}}, {{Number}}, {{USPrice}}" +
                "</div>"
            )
        }
    });
});

All help is appreciated!

Edit:

index.html:

{% extends "layout.html" %}

{% block title %}
    Main
{% endblock %}

{% block main %}
<script src="{{ url_for('static', filename='main.js') }}"></script>
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<link href="{{ url_for('static', filename='main.css') }}" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
<script src="https://cdn.rawgit.com/corejavascript/typeahead.js/master/dist/typeahead.jquery.js"></script>
{{ JSGlue.include() }}

<br>
<br>
<br>
<div>
    <h4>Search for any Star Wars Themed Lego Set</h4>
    <br>
    <br>
</div>
<div class=form-group>
    <label class="sr-only" for="q">Name, Number, USPrice</label>
    <input type="text" name="q" placeholder="Any set descriptor...">
</div>
<br>
<br>
<hr>
<p>You can search for lego sets via this search tool using descriptors of a set such as Set Name, Set Number, Set Subtheme, Year released, Number of Pieces, and Price. This will take you to a description page of the lego, where you can add it to your saved sets and look at specifications of the set. For more information, check out the about page.</p>
</hr>
{% endblock %}

requirements.txt:

Flask
Flask-JSGlue
Flask-Session

application.py headers:

from cs50 import SQL
from flask import Flask, flash, redirect, render_template, request, session, url_for, jsonify
from flask_bootstrap import Bootstrap
from flask_session import Session
from passlib.apps import custom_app_context as pwd_context
from tempfile import mkdtemp
from flask_jsglue import JSGlue
import random
import smtplib
import re


from helpers import *

app = Flask(__name__)
Bootstrap(app)
jsglue = JSGlue(app)

layout.html headers:

    <meta charset="utf-8"/>
    <meta content="initial-scale=1, width=device-width" name="viewport"/>

    <!-- documentation at http://getbootstrap.com/, alternative themes at https://www.bootstrapcdn.com/bootswatch/ -->
    
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

    <script src="https://code.jquery.com/jquery-latest.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.5/validator.min.js"></script>
7
  • 1
    Do you include the library for handlebars?
    – sk.76
    Aug 12 '17 at 11:56
  • @sk.76 No, but I did now XD. So that fixed the error in the console, but the typeahead is still not working. Any suggestions?
    – Jason_V
    Aug 12 '17 at 17:20
  • Do you include all the required libraries for jquery, typeahead, bootstrap(css,js) if u happen to be using bootstrap? You may consider having a look to index.html in pset8 for these libraries. Also, make sure u have Flask, Flask-Session, Flask-JSGlue in requirements.txt before runnning pip install commands for requirements.txt. I am assuming that you have included JSGlue(app) in application.py.
    – sk.76
    Aug 13 '17 at 7:22
  • @sk.76 Yes, I have already included all that you mentioned. Again, typeahead is not working. Just to clarify, I have posted index.html, part of application.py that includes headers, part of my layout template, and requirements.txt.
    – Jason_V
    Aug 13 '17 at 14:17
  • 1
    @Jason_V as far as I can tell you are using getJSON() to make an Ajax request but handling that request in the "POST" section. link. According to the JQUERY docpAge on that function it makes GET requests. What if you handle the submissions in the index function's GET section?
    – johnnyd3
    Aug 14 '17 at 0:40
1

The main issue I can see if you are making an Ajax request via JQuery's getJSON() method. This method makes a GET Request.

Handling this in the POST section of your index function will cause the request to always "fall through" to the else:

@app.route("/")
@login_required
def index():
    if request.method == "POST":
        q = request.form.get("search") + "%"
        set_results = db.execute("SELECT * from all_sets WHERE Name LIKE :q OR Number LIKE :q OR Year LIKE :q OR Subtheme LIKE :q OR Pieces LIKE :q OR USPrice LIKE :q", q=q)
        return jsonify(set_results)
    else:
        return render_template("index.html") 

Two ways to remedy this:

  1. You can change from using getJSON() to using the ajax() function it is wrapper for, and use the POST HTTP method:

    $.ajax({
      dataType: "json",
      url: url,
      data: data,
      type: "POST"
    }).done(function(response){
        // handler code here
    
    }).always(function(response){
       // code to always fire (optional)
    });
    

    The one change being to make your ajax request a POST instead of a GET.

  2. OR you could continue using getJSON() but change your index function to make a search for the request variable you are sending to the server. The pseudocode should look something like this:

    // q being the name of your ajax request variable
    if "q" in request.args and request.method == "GET":
       // query the db and process request
    else:
      //display page
    

    The logic here is that if the request isn't your ajax one (e.g. just a regular GET request for your page) it will not contain the key "q" and it will fall through to the else condition.

    Another way is to check the request type rather than look for the variable you are sending. It is probably cleaner since you won't have to keep track of making sure the form and the ajax request variables have different names:

    // q being the name of your ajax request variable
    if request.is_xhr and request.method == "GET":
        // query the db and process request
    else:
        //display page  
    

Some Tools:

I have found it very helpful to use console.log() in the javascript to check that the server is sending me back what I expect and also the requests section of the console (here is what they look like in chrome 60):

Console.log in Chrome 60: Console.log in Chrome 60
Requests section, under network tab, of the developer console in Chrome 60:
Network Tab of Console, requests section

Also, on the server side code using a logging module (Here is a StackOverflow Question related to that: https://stackoverflow.com/questions/32550487/how-to-print-from-flask-app-route-to-python-console ) to print variables out to the terminal. This has saved me from a lot of hair tearing out while trying to get a website to work on appengine.

1
  • How do you use both Session and JSGlue? Can you use both Session(app) and JSGlue(app)
    – Adam
    Dec 5 '17 at 19:30

You must log in to answer this question.

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