1

For my final project (a website where people can write diaries and read others' diaries), I am trying to implementing a search box where the user may search usernames to read others' diaries, so I took what I learned from Problem Set 8 to write up this drop-down search box, but the drop-down part is not working.

Application.py

@app.route("/search", methods=["GET", "POST"])
@login_required
def search():
    """Search published diaries from others."""

    # if user reached route via POST (as by submitting a form via POST)
    if request.method == "POST":

        # ensure friends' usernames were submitted
        if not request.form.get("search"):
            return dumbo("No username entered")

        # search user's friends from database
        # append % to q so that user doesn't have to input city/state/postal code in the URL completely/accurately
        # https://www.tutorialspoint.com/sqlite/sqlite_like_clause.htm
        friend = request.form.get("search") + "%"
        searches = db.execute("SELECT username FROM writers WHERE username LIKE :friend", friend=friend)

        # output a JSON array of objects, each represents a row from TABLE writers that matches what the user looks for
        return jsonify(searches)

    # else if user reached route via GET (as by clicking a link or via redirect)
    else:
        return render_template("search.html")

scripts.js

$("#friend").keydown(function() {

    // configure typeahead
    $("#friend").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>" +
                "<div>{{username}}</div>" +
                "</div>"
            )
        }
    });
    // action after friend is selected from drop-down
    $("#friend").on("typeahead:selected", function(eventObject, suggestion, name) {

        // visit friend's diaries (to be continued)

    });
});

/**
 * Searches database for typeahead's suggestions.
 */
// search calls asyncResults after searching is done (asynchronously)
function search(query, syncResults, asyncResults)
{
    // get usernames matching query (asynchronously)
    var parameters = {
        friend: $("#friend").val()
    };
    $.getJSON(Flask.url_for("search"), parameters)
    .done(function(data, textStatus, jqXHR) {

        // call typeahead's callback with search results (i.e., friends)
    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([]);
    });
}

search.html

{% extends "layout.html" %}

{% block title %}
    Search
{% endblock %}

{% block main %}
    <form action="{{ url_for('search') }}" method="post">
        <fieldset>
            <div class="form-group">
                <input autocomplete="off" autofocus class="typeahead" id="friend" name="search" placeholder="Search friends" type="text"/>
            </div>
            <div class="form-group">
                <button class="btn btn-default" type="submit">Search</button>
            </div>
            <!-- https://www.mkyong.com/javascript/how-to-link-an-external-javascript-file/ -->
            <script type="text/javascript" src="static/scripts.js"></script>
        </fieldset>
    </form>
{% endblock %}

I looked at Link1 and Link2, but those do not seem fit here given that I have to access the database in the server to complete the entire search action.

Here is my layout.html:

<!DOCTYPE html>

<html lang="en">

    <head>

        <!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta -->
        <meta charset="utf-8"/>
        <meta content="initial-scale=1, width=device-width" name="viewport"/>

        <!-- http://getbootstrap.com/ -->
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

        <!-- app's own CSS -->
        <link href="{{ url_for('static', filename='styles.css') }}" rel="stylesheet"/>

        <!-- http://jquery.com/ -->
        <script src="https://code.jquery.com/jquery-latest.min.js"></script>

        <!-- http://getbootstrap.com/ -->
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.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.5/handlebars.min.js"></script>

        <!-- http://stewartjpark.com/Flask-JSGlue/ -->
        {{ JSGlue.include() }}

        <!-- app's own JavaScript -->
        <script src="{{ url_for('static', filename='scripts.js') }}"></script>

        <title>FoToM: {% block title %}{% endblock %}</title>

    </head>

    <body>

        <div class="container">

            <nav class="navbar navbar-default">
                <div class="container-fluid">
                    <div class="navbar-header">
                        <button aria-expanded="false" class="navbar-toggle collapsed" data-target="#navbar" data-toggle="collapse" type="button">
                            <span class="sr-only">Toggle navigation</span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                        <a class="navbar-brand" href="{{ url_for('.index', username=session.get('user_username')) }}"><span class="blue">F</span><span class="yellow">o</span><span class="blue">T</span><span class="yellow">o</span><span class="blue">M</span>
                    </div>
                    <div class="collapse navbar-collapse" id="navbar">
                        {% if session.user_id %}
                            <ul class="nav navbar-nav">
                                <li><a href="{{ url_for('.news', username=session.get('user_username')) }}">News</a></li>
                                <li><a href="{{ url_for('.write', username=session.get('user_username')) }}">Write</a></li>
                                <li><a href="{{ url_for('.search', username=session.get('user_username')) }}">Search</a></li>
                            </ul>
                            <ul class="nav navbar-nav navbar-right">
                                <li><a href="{{ url_for('.reenter', username=session.get('user_username')) }}">Reset Password</a></li>
                                <li><a href="{{ url_for('logout') }}">Log Out</a></li>
                            </ul>
                        {% else %}
                            <ul class="nav navbar-nav navbar-right">
                                <li><a href="{{ url_for('register') }}">Register</a></li>
                                <li><a href="{{ url_for('login') }}">Log In</a></li>
                            </ul>
                        {% endif %}
                    </div>
                </div>
            </nav>

            {% if get_flashed_messages() %}
                <header>
                    <div class="alert alert-info" role="alert">
                            {{ get_flashed_messages() | join(" ") }}
                    </div>
                </header>
            {% endif %}

            <main>
                {% block main %}{% endblock %}
            </main>

        </div>

    </body>

</html>

Please advise, thank you!

14
  • The problem lies here: suggestion: Handlebars.compile( "<div>" + "<div>{{username}}</div>" + "</div>" ) Change "<div>{{username}}</div>" to "{{username}}". – sk.76 Aug 3 '17 at 13:12
  • Did you ever fix this problem? I'm having a similar issue... – Jason_V Aug 5 '17 at 1:26
  • Would you mind elaborating more on that part? @sk.76 – Alex Chan Aug 5 '17 at 5:16
  • 1
    No not yet, @Jason_V, I am still working on it! – Alex Chan Aug 5 '17 at 5:17
  • @AlexChan I mean u don't have to include div tags twice in the Javascript argument u pass to Handlebars.compile method. This is to say that instead of doing ' "<div>" + "<div>{{username}}</div>" + "</div>" ' you could go for ' "<div>" + "{{username}}" +"</div>" '. Try and let me know if it works. – sk.76 Aug 5 '17 at 6:55
0

You may consider the following stuff to change:

  1. <script type="text/javascript" src="static/scripts.js"></script> to <script src="{{ url_for('static', filename='scripts.js') }}"></script> as the file is served from a static folder

  2. Include the libarary neede for handlebars and typeahead to work in layout.html <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script> and <script src="https://cdn.rawgit.com/corejavascript/typeahead.js/master/dist/typeahead.jquery.js"></script>

  3. Change <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> to <script src="https://code.jquery.com/jquery-latest.min.js"></script> because the latest version is 3.2.1 and infact you don't need to keep track of latest version, the latter will suffice.

  4. In "layout.html" include {{ JSGlue.include() }} before including style.css and scripts.js so as to hook your flask application nicely with the front end.But before you do this make sure that in application,py you have to include JSGlue(app) after you instanciate your Flask app. You may post your code for the beginning part of application.py before the index route, so that I can see how do you configure your Flask app because if you don't do that properly you won't be able to serve the user as needed.

8
  • Hi @sk.76, I need everything you did (thank you so much!). (1) The dropdown menu is still not working though. (2) And there is another issue, which is that after adding the typeahead library as instructed (pursuant to your step 2), my search box does not allow input. The user cannot type in the search box. I really want to turn over to you all my codes for your convenience. Would you mind sending me an email at alexchan@berkeley.edu, so that I can send you the link to access my complete code for the final project? Thanks again, I really, really appreciate it! – Alex Chan Aug 10 '17 at 5:48
  • As an update, the second issue is fixed. Latest version of code is reflected above (to fix the second issue, I just had to reorder how different external JS libraries are added to layout.html). Dropdown menu is still not dropping down options though. I opened up the console in an effort to debug, but the console did not identify any red flags. @sk.76 – Alex Chan Aug 12 '17 at 5:43
  • Did you include all those I told u in dropbox message in requirements.txt and run pip3 install --user -r requirements.txt before running falsk? – sk.76 Aug 12 '17 at 7:11
  • Yes I did! @sk.76 – Alex Chan Aug 13 '17 at 7:23
  • @AlexChan Do you include your keydown event inside of $(document).ready(function() { }); in scripts.js? – sk.76 Aug 13 '17 at 7:30

You must log in to answer this question.

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