简体   繁体   中英

Global variable not accessible to ViewModel in KnockoutJS

I am working on a simple Google Maps app using the KnockoutJS library - or at least it seemed simple enough in concept. After reading up on KnockoutJS and working out some examples, I put together the initial parts. The html so far is just the map, I aim to populate the list as soon as I get over the first hurdle. The hurdle is in the Javascript. Here is the code for reference:

"use strict";

var map;
var center;
var defaultBounds;
var placesServices;

//LocationObject created to hold data for locations generated from google.places.nearbySearch
var LocationObject = function(data){

    this.position = data.geometry.location;
    this.lat = data.geometry.location.lat;
    this.lng = data.geometry.location.lng;
    this.name = data.name;
    this.placeID = data.place_id;
    this.rating = data.rating;
    this.types = data.types;
    this.linkToPhoto = data.html_attributions;

};



function initMap(){

    map = new google.maps.Map(document.getElementById('map'), {
        center: center,
        zoom: 17,
        mapTypeId: 'satellite',
        draggable: true, 
        zoomControl: false, 
        scrollwheel: true, 
        disableDoubleClickZoom: true
    });

    defaultBounds = new google.maps.LatLngBounds(
        new google.maps.LatLng(47.614217, -122.317981),new google.maps.LatLng(47.612975, -122.316291));
    map.fitBounds(defaultBounds);

    placesServices = new google.maps.places.PlacesService(map);

}


    //ViewModel created to observe changes on the map
var MapViewModel = function(){
    var self = this;

    self.testarr = ko.observableArray([
        { firstName: 'Bert', lastName: 'Bertington' },
        { firstName: 'Charles', lastName: 'Charlesforth' },
        { firstName: 'Denise', lastName: 'Dentiste' }
    ]);

    self.locations = ko.observableArray([]);
    self.markers = [];

    this.getNearbyLocations = function(){
        if(placesServices === undefined){
            placesServices = new google.maps.places.PlacesService(map);
        }

        var request = {
            location: center,
            radius: getBoundsRadius(defaultBounds),
            type: ['establishment']
        };

        placesServices.nearbySearch(request, function(results, status) {
            if (status === google.maps.places.PlacesServiceStatus.OK) {
                for (var i = 0; i <= 18; i++) {
                    var marker = new google.maps.Marker({
                        map: map,
                        position: results[i].geometry.location,
                        animation: google.maps.Animation.DROP
                    });
                    self.locations.push(new LocationObject(results[i]));
                    self.markers.push(marker);
                }
            } else{
                alert("We were not able to find any nearby locations in this Neighbourhood.");
            }
        });
    };

    this.init = function(){

        self.getNearbyLocations();

    };
};


var myMapViewModel = new MapViewModel();
myMapViewModel.init();
ko.applyBindings(myMapViewModel);   



/* Utility Functions */
function getBoundsRadius(bounds){....}

The error is:

Uncaught ReferenceError: google is not defined
at MapViewModel.getNearbyLocations

and I don't get why it's not being recognized here, when the map itself loads first without issue but here it get's hung up.

Here is the html for reference, but there shouldn't be an issue there:

<!DOCTYPE html>
<html lang="en">
  <head>
   <!-- Required meta tags -->
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

   <!-- Per Google guideline scheduled for the M65 release, css has been moved to an importer -->
   <link rel="import" href="importer.htm"></link>

   <!-- Bootstrap CSS -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">


  </head>
  <body>
   <div class="container-fluid">
    <div class="row">
        <div class="col-xs-12 col-md-4">
            <!-- List goes in this column. -->

            </div>
        </div>
        <div class="col-xs-12 col-md-8">
            <!-- Map goes into this column. -->
            <div id="map" style="width:100%;height:100vh;"></div>
        </div>
      </div>
     </div>

<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<!-- Optional JavaScript -->
<script src="js/knockout.js" ></script>
<script src="js/app.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-1EdIyUOb74oGG_mEoPvJTAGCSJvSQms&callback=initMap&libraries=places"></script>
 </body>

I also thought that the error was because your app.js is being called before the maps script. But I tried changing it, same error, and I also tried removing the async parameter (as suggested by user3297291's comment), still the same error. Theoretically it should have worked .

But this worked - wrap the last 3 lines of your app.js code inside a ready() function:

$(document).ready(function(){
    var myMapViewModel = new MapViewModel();
    myMapViewModel.init();
    ko.applyBindings(myMapViewModel); 
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM