Nicht verarbeiten Bindung - binden keine Funktion
Ich bin mit knockoutjs für meine single-page-Anwendung, und ich bin derzeit fest auf einem mysteriösen problem.
Ich versuche die Anzeige eines dropdown-Menü, und füllen Sie es mit Knock-out-Bindungen. Für diesen Zweck, ich benutze ein, dass foreach iteriert über alle Elemente:
<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>
Dann, in meinem viewModel, ich habe die openInfoWindow-Funktion (die soll aufgerufen werden, wenn ein dropdown-Element geklickt wird):
//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!");
}
}
Das problem ist, wenn ich hinzufügen klicken Sie auf: openInfoWindow Bindung auf das dropdown-item-element, bekomme ich die folgende Fehlermeldung:
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 binding funktioniert perfekt, auf seine eigene.
Wo hast begehe ich einen Fehler?
EDIT:
Hier gibt es mehr details über die Umsetzung. Beachten Sie, dass die Karte div-Karte verwendet die Google Maps API.
<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>
Hier gibt es mehr details zu den jeweiligen javascript verwendet.
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());
Du musst angemeldet sein, um einen Kommentar abzugeben.
EDIT 2:
Mit den aktualisierten source code, den ich erzeugen konnte, die ein jsFiddle, dass reproduziert das problem. fiddle
Scheint es, die slim version von jQuery, die Sie laden, fehlen ein paar Funktionen, die knockout davon ausgegangen, dort zu sein. Speziell in diesem Fall der ".bind" - Funktion erscheint innerhalb der "foreach" - Bindung. Wenn Sie ersetzen Sie das Skript mit der standard-jquery-das sollte klar bis.
EDIT 1:
In diesem Fall das problem nicht mit den code, den Sie geschrieben. Hier ist eine funktionierende snippet, welches ich warf zusammen aus dem code oben, außer für die anfängliche "mFavPlaces.forEach - " was ich nicht haben Zugang zu.
JS:
HTML:
Original Antwort:
Den Kontext für die Bindung an dieser Stelle ist der einzelne Platz. Das name binding funktioniert, weil der name ist eine Eigenschaft, die auf jedem Platz. Ihre Klick-Funktion, erscheint jedoch auf der Eltern-Ansicht-Modell. Sie können ändern, klicken Sie auf verbindlich zu sein:
data-bind="text: name, click: $parent.openInfoWindow"
und es sollte funktionieren.Für diejenigen, die nicht mit
jquery.slim
, bauen Ihre Lösung mit Hilfe bedürfen. Die folgenden shim wird dein problem lösen:Den Fehler angezeigt in der console geschieht aufgrund einer race-Bedingung beim laden der jQuery und Knockout.