Javascript 无法处理绑定-绑定不是函数

Javascript 无法处理绑定-绑定不是函数,javascript,knockout.js,onclick,knockout-2.0,knockout-mapping-plugin,Javascript,Knockout.js,Onclick,Knockout 2.0,Knockout Mapping Plugin,我正在为我的单页应用程序使用knockoutjs,目前我遇到了一个神秘的问题 我试图显示一个下拉菜单,并使用敲除绑定填充它。为此,我使用一个foreach来迭代所有元素: <div data-bind="foreach: favPlaces" class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <a data-bind="text: name, click: $parent.openInfoWindo

我正在为我的单页应用程序使用knockoutjs,目前我遇到了一个神秘的问题

我试图显示一个下拉菜单,并使用敲除绑定填充它。为此,我使用一个foreach来迭代所有元素:

<div data-bind="foreach: favPlaces" class="dropdown-menu" aria-labelledby="dropdownMenuButton">
     <a data-bind="text: name, click: $parent.openInfoWindow" class="dropdown-item">Place Name</a>
</div>
问题是,当我将click:openInfoWindow绑定添加到下拉项元素时,出现以下错误:

Uncaught TypeError: Unable to process binding "foreach: function (){return favPlaces }"
Message: Unable to process binding "click: function (){return $parent.openInfoWindow }"
Message: u(...).bind is not a function
    at Object.p (knockout-3.4.1.js:17)
    at knockout-3.4.1.js:89
    at Object.b (knockout-3.4.1.js:9)
    at init (knockout-3.4.1.js:89)
    at init (knockout-3.4.1.js:103)
    at knockout-3.4.1.js:72
    at Object.w (knockout-3.4.1.js:39)
    at knockout-3.4.1.js:72
    at Object.q (knockout-3.4.1.js:11)
    at m (knockout-3.4.1.js:71)
text:name绑定本身就可以完美地工作

我在哪里犯了错误

编辑:

下面是关于实现的更多细节。注意,map div是一个使用googlemapsapi的映射

<body>
    <div id="full-height">
        <div id="map"></div>
        <nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md navbar-light bg-faded">
            <a class="navbar-brand" href="#">
                <i id="foursquare-logo" class="fa fa-foursquare" aria-hidden="true"></i>
            </a>
            <div id="location-dropup" class="btn-group dropup">
                <button type="button" class="btn btn-secondary">Best locations</button>
                <button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                    <span class="sr-only">Toggle Dropdown</span>
                </button>
                <div data-bind="foreach: favPlaces" class="dropdown-menu" aria-labelledby="dropdownMenuButton">
                    <a data-bind="text: name, click: $parent.openInfoWindow" class="dropdown-item">Place Name</a>
                </div>
            </div>

        </nav>

    </div>
    <!-- Foursquare logo -->
    <script src="https://use.fontawesome.com/5228693ec0.js"></script>
    <!-- Bootstrap -->
    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
    <!-- KnockoutJS -->
    <script src="js/lib/knockout-3.4.1.js"></script>
    <script src="js/app.js"></script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDtODGjlNobKNCo4OX_voxjIkNkHCfQ3I4&callback=initMap"></script>
</body>

最佳地点
切换下拉列表
“+”
“+”地址:“+地址+”; setContent(contentString); 打开(mMap,标记); } //最喜欢的地方的对象表示 var FavPlace=函数(数据){ this.name=ko.observable(data.name); this.imgSrc=ko.可观察(data.imgSrc); } //视图模型 var TokyoViewModel=函数(){ var self=这个; //所有喜欢的地方 this.favPlaces=ko.observearray([]); mFavPlaces.forEach(函数(位置){ 自行推(新推(置)); }); this.openInfoWindow=函数(favPlace){ console.log(“成功!”); } } //初始化地图并使用信息窗口添加标记 函数initMap(){ var中心={lat:35.6809814,lng:139.7538745}; mMap=new google.maps.Map(document.getElementById('Map'){ 缩放:12, 中心:中心 }); //获取最喜欢的地方的详细信息 var指数; 对于(placeIndex=0;placeIndex
编辑2:

通过您更新的源代码,我能够创建一个再现问题的JSFIDLE

您正在加载的jQuery的slim版本似乎缺少了knockout假定存在的一些函数。特别是在这种情况下,.bind函数似乎是在“foreach”绑定中使用的。如果用标准jquery替换此脚本,则应该清除该脚本

编辑1:


谢谢你的回答。这实际上是我的一个错误,我忘了更新我的代码。实际上我已经有了$parent.openInfoWindow。我也更新了错误日志

在这种情况下,问题不在于您发布的任何代码。这是我从上面的代码中拼凑出来的一个工作片段,除了我无法访问的初始“mFavPlaces.forEach”

//视图模型
var TokyoViewModel=函数(){
var self=这个;
//所有喜欢的地方
this.favPlaces=ko.observearray([]);
//...
self.favPlaces.push(新FavPlace(“名字在这里?”);
this.openInfoWindow=函数(favPlace){
console.log(“成功!”);
}
}
var FavPlace=函数(名称){
//未知视图模型
var self=这个;
self.name=ko.observable(名称);
}
应用绑定(新TokyoViewModel())

地名

适用于未使用jquery.slim的用户,他们正在使用require构建解决方案。以下垫片将解决您的问题:

require.config({
    //rest of config
    shim: {
        knockout: {
            deps: ["jquery"],
            exports: "knockout"
        }
    }
    //rest of config
});

控制台中显示的错误是由于加载jQuery和Knockout时的竞争条件造成的。

感谢您的回答。这实际上是我的一个错误,我忘了更新我的代码。实际上我已经有了$parent.openInfoWindow。我还更新了错误日志。@Lois我看不出您当时显示的代码有任何问题。你会想发布一个更完整的例子来重现这个问题。谢谢!你在jQuery的超薄版本上的评论救了我一天!!我有一个类似于TypeError:u(…)的错误。bind不是一个函数
var mFavPlaces = [

    {
        name: "Takeshita Street",
        lat: 35.6715659,
        lng: 139.7031469,
        imgSrc: "img/favPlaces/takeshita.jpg"
    }, {
        name: "Nakamise Street",
        lat: 35.7113873,
        lng: 139.794207,
        imgSrc: "img/favPlaces/asakusa.jpg"
    }, {
        name: "Yodobashi-Akiba",
        lat: 35.6995227,
        lng: 139.7734171,
        imgSrc: "img/favPlaces/akihabara.jpg"
    }, {
        name: "Meiji Jingu",
        lat: 35.6763976,
        lng: 139.6993259,
        imgSrc: "img/favPlaces/meiji.jpg"
    }, {
        name: "Shibuya Crossing",
        lat: 35.6594087,
        lng: 139.6981677,
        imgSrc: "img/favPlaces/shibuya.jpg"
    }

];

// Stores the Google maps markers for the favPlaces
var mMarkers = {};

var mQueryInfo = {
    "near": "Tokyo",
    "client_id": "AG5MATDOQ5HAXLODDIV1YALJZA4IN3LS5XEUOPWQIGHG0BHL",
    "client_secret": "PPJYHED0SI5WLWC05LXGD1E3T1JDQI23EWNSTQLI2MO0WEAF",
    "version": "20170220"
};

function httpGetAsync(theUrl, callback, infoWindow, placeIndex, marker) {
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
            callback(xmlHttp.responseText, infoWindow, placeIndex, marker);
        }
    }
    xmlHttp.open("GET", theUrl, true); // true for asynchronous 
    xmlHttp.send(null);
}

function setFavPlaceInfo(placeName, infoWindow, placeIndex, marker) {
    var url = "https://api.foursquare.com/v2/venues/search?limit=1&near=" + mQueryInfo.near + "&query=" + placeName + "&v=" + mQueryInfo.version + "&client_id=" + mQueryInfo.client_id + "&client_secret=" + mQueryInfo.client_secret;
    httpGetAsync(url, setInfoWindowContent, infoWindow, placeIndex, marker);
}

function setInfoWindowContent(placeDetailsText, infoWindow, placeIndex, marker) {

    var placeDetails = JSON.parse(placeDetailsText);

    // Extract info to display in infoWindows
    var completeName = placeDetails.response.venues[0].name;
    var address = placeDetails.response.venues[0].location.address;
    var websiteUrl = placeDetails.response.venues[0].url;
    var numberCheckins = placeDetails.response.venues[0].stats.checkinsCount;

    var contentString = '<div class="infoWindow">' + '<img class="img-fluid img-thumbnail" src="' + mFavPlaces[placeIndex].imgSrc + '" style="margin-bottom:1rem;" alt="' + completeName + '" />' + '<h4>' + completeName + '</h4>' + '<b>Checkins: </b>' + numberCheckins + '<br>' + '<b>Website: </b> <a href="' + websiteUrl + '">' + websiteUrl + '</a>' + '<br>' + '<b>Address: </b> ' + address + '</div>';

    infoWindow.setContent(contentString);
    infoWindow.open(mMap, marker);
}

// Object representation of a favorite place
var FavPlace = function(data) {
    this.name = ko.observable(data.name);
    this.imgSrc = ko.observable(data.imgSrc);
}

// View Model
var TokyoViewModel = function() {
    var self = this;

    // All the favorite places
    this.favPlaces = ko.observableArray([]);
    mFavPlaces.forEach(function(place) {
        self.favPlaces.push(new FavPlace(place));
    });

    this.openInfoWindow = function(favPlace) {
        console.log("Success!");
    }
}

// Initialize the map and adds markers with infoWindows
function initMap() {

    var center = { lat: 35.6809814, lng: 139.7538745 };
    mMap = new google.maps.Map(document.getElementById('map'), {
        zoom: 12,
        center: center
    });

    // Get details of favorite places 
    var placeIndex;
    for (placeIndex = 0; placeIndex < mFavPlaces.length; placeIndex++) {
        var marker = new google.maps.Marker({
            position: { lat: mFavPlaces[placeIndex].lat, lng: mFavPlaces[placeIndex].lng },
            map: mMap,
        });

        var infowindow = new google.maps.InfoWindow({});

        // Use a closure to add listeners
        google.maps.event.addListener(marker, 'click', (function(marker, placeIndex) {
            return function() {
                // Set infoWindow's content
                setFavPlaceInfo(mFavPlaces[placeIndex].name, infowindow, placeIndex, marker);

                // Set marker animation (lasts for 1 cycle == 750ms)
                marker.setAnimation(google.maps.Animation.BOUNCE);
                setTimeout(function() { marker.setAnimation(null); }, 750);
            }
        })(marker, placeIndex));

        // Store the marker
        mMarkers[mFavPlaces[placeIndex].name] = marker;
    }


}

// Activates knockout.js
ko.applyBindings(new TokyoViewModel());
require.config({
    //rest of config
    shim: {
        knockout: {
            deps: ["jquery"],
            exports: "knockout"
        }
    }
    //rest of config
});