-2

So, I am building a web app that uses google maps's API to display a map with some markers on it, every time you click on a marker, it is supposed to show 5 articles from that location, these provided by google news, the thing is, I am pretty new to javascript and web development and I've been trying for hours and hours to solve this problem on my own and I don't know how. I'll leave both my JS file and my python file that uses flask to handle server requests, but I'm really sure there's nothing wrong in the server side.

js:

// Google Map
var map;

// markers for map
var markers = [];

// info window
var info = new google.maps.InfoWindow();

// execute when the DOM is fully loaded
$(function() {

    // styles for map
    // https://developers.google.com/maps/documentation/javascript/styling
    var styles = [

        // hide Google's labels
        {
            featureType: "all",
            elementType: "labels",
            stylers: [
                {visibility: "off"}
            ]
        },

        // hide roads
        {
            featureType: "road",
            elementType: "geometry",
            stylers: [
                {visibility: "off"}
            ]
        }

    ];

    // options for map
    // https://developers.google.com/maps/documentation/javascript/reference#MapOptions
    var options = {
        center: {lat: 42.3770, lng: -71.1256}, // Cambridge, MA
        disableDefaultUI: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        maxZoom: 14,
        panControl: true,
        styles: styles,
        zoom: 13,
        zoomControl: true
    };

    // get DOM node in which map will be instantiated
    var canvas = $("#map-canvas").get(0);

    // instantiate map
    map = new google.maps.Map(canvas, options);

    // configure UI once Google Map is idle (i.e., loaded)
    google.maps.event.addListenerOnce(map, "idle", configure);

});

/**
 * Adds marker for place to map.
 */
function addMarker(place)
{
    // initialize marker
    var marker = new google.maps.Marker({
        position: {lat: place.latitude, lng: place.longitude},
        map: map,
        title: place.place_name + ', ' + place.admin_name1,
        icon: "http://maps.google.com/mapfiles/kml/pal2/icon31.png"
    });

    // add marker with its place to markers array
    markers.push({marker, place});

    index = markers.length - 1

    // add event listener to the marker
    google.maps.event.addListener(markers[index].marker, 'click', showArticles(markers[index]))
}

/**
 * Gets the articles to be displayed
 */
function showArticles(local)
{
    var parameters = {
        geo: local.place.place_name
    };

    // get articles for the place
    json = $.getJSON(Flask.url_for("articles"), parameters);

    // store those articles in a string containing html
    html = "<ul>"

    json.done(function (){

        if(json.responseJSON){
            for (var i = 0; i < json.responseJSON.length; i++){
            html += "<li><a src=\"" + json.responseJSON[i].link + "\">" + json.responseJSON[i].title + "</a></li>";
            }

            html += "</ul>"

            console.log(json.responseJSON)
            console.log(html)

            showInfo(local.marker, html)

        }}).fail(function (){
            showInfo(local.marker, "")
        })

}

/**
 * Configures application.
 */
function configure()
{
    // update UI after map has been dragged
    google.maps.event.addListener(map, "dragend", function() {

        // if info window isn't open
        // http://stackoverflow.com/a/12410385
        if (!info.getMap || !info.getMap())
        {
            update();
        }
    });

    // update UI after zoom level changes
    google.maps.event.addListener(map, "zoom_changed", function() {
        update();
    });

    // configure typeahead
    $("#q").typeahead({
        highlight: false,
        minLength: 1
    },
    {
        display: function(suggestion) { return null; },
        limit: 10,
        source: search,
        templates: {
            suggestion: Handlebars.compile(
                "<div>" +
                "{{place_name}}, {{admin_name1}}, {{postal_code}}" +
                "</div>"
            )
        }
    });

    // re-center map after place is selected from drop-down
    $("#q").on("typeahead:selected", function(eventObject, suggestion, name) {

        // set map's center
        map.setCenter({lat: parseFloat(suggestion.latitude), lng: parseFloat(suggestion.longitude)});

        // update UI
        update();
    });

    // hide info window when text box has focus
    $("#q").focus(function(eventData) {
        info.close();
    });

    // re-enable ctrl- and right-clicking (and thus Inspect Element) on Google Map
    // https://chrome.google.com/webstore/detail/allow-right-click/hompjdfbfmmmgflfjdlnkohcplmboaeo?hl=en
    document.addEventListener("contextmenu", function(event) {
        event.returnValue = true;
        event.stopPropagation && event.stopPropagation();
        event.cancelBubble && event.cancelBubble();
    }, true);

    // update UI
    update();

    // give focus to text box
    $("#q").focus();
}

/**
 * Removes markers from map.
 */
function removeMarkers()
{
    for(var i = 0; i < markers.length; i++){
        markers[i].marker.setMap(null)
    }

    markers = []
}

/**
 * Searches database for typeahead's suggestions.
 */
function search(query, syncResults, asyncResults)
{
    // get places matching query (asynchronously)
    var parameters = {
        q: query
    };
    $.getJSON(Flask.url_for("search"), 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([]);
    });
}

/**
 * Shows info window at marker with content.
 */
function showInfo(marker, content)
{
    // start div
    var div = "<div id='info'>";
    if (typeof(content) == "undefined")
    {
        // http://www.ajaxload.info/
        div += "<img alt='loading' src='/static/ajax-loader.gif'/>";
    }
    else
    {
        div += content
    }

    // end div
    div += "</div>";

    // set info window's content
    info.setContent(div);

    // open info window (if not already open)
    info.open(map, marker);
}

/**
 * Updates UI's markers.
 */
function update()
{
    // get map's bounds
    var bounds = map.getBounds();
    var ne = bounds.getNorthEast();
    var sw = bounds.getSouthWest();

    // get places within bounds (asynchronously)
    var parameters = {
        ne: ne.lat() + "," + ne.lng(),
        q: $("#q").val(),
        sw: sw.lat() + "," + sw.lng()
    };
    $.getJSON(Flask.url_for("update"), parameters)
    .done(function(data, textStatus, jqXHR) {

       // remove old markers from map
       removeMarkers();

       // add new markers to map
       for (var i = 0; i < data.length; i++)
       {
           addMarker(data[i]);
       }
    })
    .fail(function(jqXHR, textStatus, errorThrown) {

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

};

Python:

import os
import re
from flask import Flask, jsonify, render_template, request, url_for
from flask_jsglue import JSGlue

from cs50 import SQL
from helpers import lookup

# configure application
app = Flask(__name__)
JSGlue(app)

# ensure responses aren't cached
if app.config["DEBUG"]:
    @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 CS50 Library to use SQLite database
db = SQL("sqlite:///mashup.db")

@app.route("/")
def index():
    """Render map."""
    if not os.environ.get("API_KEY"):
        raise RuntimeError("API_KEY not set")
    return render_template("index.html", key=os.environ.get("API_KEY"))

@app.route("/articles")
def articles():
    """Look up articles for geo."""

    # get geo
    geo = request.args.get("geo")

    # if there is no geo, return error
    if geo == '' or geo == None:
        raise RuntimeError("missing geo")

    # get articles for this geo
    articles = lookup(geo)

    if len(articles) > 5:
        articles = articles[:5]

    # return them as a json object
    return jsonify(articles)

@app.route("/search")
def search():
    """Search for places that match query."""

    place = request.args.get("q") + "%"

    # get everything that mathes this query
    places = db.execute("SELECT * FROM places WHERE postal_code LIKE :place OR place_name LIKE :place OR admin_name1 LIKE :place LIMIT 10", place=place)

    # return them as objects
    return jsonify(places)

@app.route("/update")
def update():
    """Find up to 10 places within view."""

    # ensure parameters are present
    if not request.args.get("sw"):
        raise RuntimeError("missing sw")
    if not request.args.get("ne"):
        raise RuntimeError("missing ne")

    # ensure parameters are in lat,lng format
    if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("sw")):
        raise RuntimeError("invalid sw")
    if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("ne")):
        raise RuntimeError("invalid ne")

    # explode southwest corner into two variables
    (sw_lat, sw_lng) = [float(s) for s in request.args.get("sw").split(",")]

    # explode northeast corner into two variables
    (ne_lat, ne_lng) = [float(s) for s in request.args.get("ne").split(",")]

    # find 10 cities within view, pseudorandomly chosen if more within view
    if (sw_lng <= ne_lng):

        # doesn't cross the antimeridian
        rows = db.execute("""SELECT * FROM places
            WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude AND longitude <= :ne_lng)
            GROUP BY country_code, place_name, admin_code1
            ORDER BY RANDOM()
            LIMIT 10""",
            sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)

    else:

        # crosses the antimeridian
        rows = db.execute("""SELECT * FROM places
            WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude OR longitude <= :ne_lng)
            GROUP BY country_code, place_name, admin_code1
            ORDER BY RANDOM()
            LIMIT 10""",
            sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)

    # output places as JSON
    return jsonify(rows)

This is the error that appears on the console when I'm running the page, if needed, I can leave a link to the server where the app is running. enter image description here

  • Pictures of error messages and code are not very useful. Please provide the complete text of the error in your question (and probably would be helpful in the title as well), as well as a [mcve] that generates that error. – geocodezip Sep 11 '17 at 21:54
  • I'm sorry, that's the only thing about the error that shows up, also, if I try to remove anything from the code I'll break the app in some way. This is the link to the app, it is running at the moment, maybe you can find something in the console https://ide50-lucalopes.cs50.io , again, I'm pretty new to programming, been doing it for just over a month, web programming for just over a week, I'm sorry I can't help with more info. – LucaLopes Sep 11 '17 at 22:07
  • @geocodezip Just ask me for something more specific, I'll gladly provide it to you. – LucaLopes Sep 11 '17 at 22:13

1 Answers1

0

The following code is returning an undefined value on line 93:

// get articles for the place
json = $.getJSON(Flask.url_for("articles"), parameters);

The error occurs on line 100 when you try to access json.responseJSON. The conditionals you use will need to be something more like one of the following:

if ( typeof json != "undefined" )

or

if ( json.responseJSON.length > 0 )
Dedering
  • 413
  • 2
  • 10
  • I only try to do something with the returned value if there is a value, there is a if condition to check that. Furthermore, this part of the code is supposed to get articles for a particular place, and I receive those. – LucaLopes Sep 11 '17 at 22:04
  • On line 100, check that the json var is not undefined: if ( typeof json != "undefined" ) – Dedering Sep 11 '17 at 22:15
  • Just tried that, didn't change anything, I was having a json.responseJSON error before because it was undefined, when I added that condition it solved that problem, it's another thing – LucaLopes Sep 11 '17 at 22:24
  • If you're judging by the error image, my javascript file is called scripts.js, none of those that appear are mine, they're all from google maps' API. – LucaLopes Sep 11 '17 at 22:44