I have set up a flask project that should allow the user to create adventures which also get a location (latitude and longitude) attached to them which is stored in the database.
Now I want to give the user the option view all the adventures that have been created in a table that is searchable with a searchbar on top. There also should be a map that has markers for each adventures location on the current page placed in it. I want to integrate the pagination because of the amount of data that would be present in a deployed application.
However I can not display the map because the initMap function is not recognized. Furthermore the pagination aspect of my table also does not work as expected.
This is the route that I have defined.
@main.route('/searchAdventure', methods = ['GET', 'POST'])@login_requireddef searchAdventure(): load_dotenv() api_key = os.getenv('API_KEY') #Get page and limit from query parameteres with default values page = request.args.get('page', 1, type=int) limit = request.args.get('limit', 10, type=int) #use paginate instead of all to get the subset of adventures pagination = Adventure.query.paginate(page=page, per_page = limit, error_out=False) adventures = [adventure.to_dict() for adventure in pagination.items] latitudes = [adventure['latitude'] for adventure in adventures] longitudes = [adventure['longitude'] for adventure in adventures] if request.headers.get('X-Requested-With') == 'XMLHttpRequest': print('ajax request') return jsonify(adventures) return render_template('searchAdventures.html', adventures = adventures, api_key = api_key, latitudes=latitudes, longitudes =longitudes)
This is the searchAdventures.html.html template.
{% extends "base.html" %}<style> .map-container { height: 400px; width: 100%;}</style>{% block content %}<div class="container mt-5"><div class="row justify-content-center"><div class="col-10"><h3 class="text-center mb-4">Adventures</h3><!-- Add the input field for search --><input type="text" id="myInput" onkeyup="searchBar()" placeholder="Search for adventures..."><!-- Move the map container here --><div class="map-container" id="map"></div><script async defer src="https://maps.googleapis.com/maps/api/js?key={{ api_key }}&libraries=maps,places&v=beta&callback=initMap"></script><div class="table-responsive"><table class="table table-striped table-light-bordered-hover" id="adventureTable"><thead class="thead-dark"><tr><th scope="col">Title</th><th scope="col">Type</th><th scope="col">Dates</th><th scope="col">People Wanted</th><th scope="col">Description</th></tr></thead><tbody id="adventureBody"> {% for adventure in adventures %}<tr><td>{{adventure['title']}}</td><td>{{adventure['type']}}</td><td>{{ adventure['startdate'] }} - {{ adventure['enddate'] }}</td><td>{{ adventure['people_wanted'] }}</td><td>{{ adventure['description'] }}</td></tr> {% endfor %}<!-- Adventures will be appended here--></tbody></table></div><script> var latitudes = JSON.parse('{{ latitudes|safe }}'); var longitudes = JSON.parse('{{ longitudes|safe }}'); var adventures = JSON.parse('{{ adventures|tojson|safe }}'); function initMap() { var map = new google.maps.Map(document.getElementById('map'), { center: { lat: parseFloat(latitudes[0]), lng: parseFloat(longitudes[0]) }, zoom: 12 }); for (var i = 0; i < adventures.length; i++) { var marker = new google.maps.Marker({ position: { lat: parseFloat(latitudes[i]), lng: parseFloat(longitudes[i]) }, map: map, title: adventures[i]['title'], adventureId: adventures[i]['id'] }); } } var page = 2; var limit = 10; function loadAdventures(scrolling = false) { console.log('loading adventures'); $.ajax({ url: '/searchAdventures', type: 'GET', data: { page: page, limit: limit }, dataType: 'json', success: function (data) { var tbody = $('#adventureBody'); if (!scrolling) { // Clear the existing table if not scrolling tbody.empty(); } for (var i = 0; i < data.length; i++) { var adventure = data[i]; var tr = $('<tr>'); tr.append('<td>'+ adventure['title'] +'</td>'); tr.append('<td>'+ adventure['type'] +'</td>'); tr.append('<td>'+ adventure['startdate'] +' - '+ adventure['enddate'] +'</td>'); tr.append('<td>'+ adventure['peoplewanted'] +'</td>'); tr.append('<td>'+ adventure['description'] +'</td>'); tbody.append(tr); } page++; } }); } $(window).scroll(function () { console.log('scrolling'); if ($(window).scrollTop() + $(window).height() > $(document).height() - 100) { loadAdventures(true); // Pass true to indicate scrolling } }); loadAdventures(); function searchBar() { // Declare variables var input, filter, table, tr, td, i, txtValue; input = document.getElementById('myInput'); filter = input.value.toUpperCase(); table = document.getElementById('adventureTable'); tr = table.getElementsByTagName('tr'); // Loop through all table rows, and hide those who don't match the search query for (i = 0; i < tr.length; i++) { td = tr[i].getElementsByTagName('td')[0]; // Change the index based on the column you want to search if (td) { txtValue = td.textContent || td.innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ''; } else { tr[i].style.display = 'none'; } } } }</script></div></div></div>{% endblock %}
This is my base.html template that each page extends.
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Adventure Buddies</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script><script src="https://unpkg.com/@popperjs/core@2"></script><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script></head><body class="d-flex flex-column min-vh-100"><header><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container"><a class="navbar-brand" href="{{ url_for('main.index') }}">Adventure Buddies</a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMenuHeroA" aria-controls="navbarMenuHeroA" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarMenuHeroA"><ul class="navbar-nav ml-auto"><li class="nav-item"><a class="nav-link" href="{{ url_for('main.index') }}">Home</a></li> {% if current_user.is_authenticated %}<li class="nav-item"><a class="nav-link" href="{{ url_for('main.user_adventures') }}">Your Adventures</a></li><li class="nav-item"><a class="nav-link" href="{{ url_for('auth.profile') }}">Profile</a></li><li class="nav-item"><a class="nav-link" href="{{ url_for('auth.logout') }}">Logout</a></li> {% else %}<li class="nav-item"><a class="nav-link" href="{{ url_for('auth.login') }}">Login</a></li><li class="nav-item"><a class="nav-link" href="{{ url_for('auth.signup') }}">Sign Up</a></li> {% endif %}</ul></div></div></nav></header><section class="hero bg-primary text-white flex-grow-1"><div class="container text-center my-auto"> {% block content %} {% endblock %}</div></section></body></html>
I always get the same error indicating that the callback is not possible because the function is not found: Uncaught (in promise) InvalidValueError: initMap is not a functionat _.yj (js?key=AIzaSyAZXpI7p0VPxwqkKBVcVYDamHbgOMi6HR8&libraries=maps,places&v=beta&callback=initMap:206:368)at lca (js?key=AIzaSyAZXpI7p0VPxwqkKBVcVYDamHbgOMi6HR8&libraries=maps,places&v=beta&callback=initMap:293:462)at js?key=AIzaSyAZXpI7p0VPxwqkKBVcVYDamHbgOMi6HR8&libraries=maps,places&v=beta&callback=initMap:293:15.Any help would be highly appreciated!