I have a method which returns promise with an array of complex objects via yelp api. I need to bind it with markup by data-bind="foreach: objects", but I can't. I need to understand how to bind data in markup, and how to work with promises in observable arrays. Can anyone help?
//getDataForPlaces var getDataForPlaces = function(addresses){ return Promise.all(Array.prototype.map.call(addresses, function(address) { return getLocationDesc(address); })); }; //getLocationDesc var getLocationDesc = function(address){ return new Promise(function(resolve, reject) { var parameters = []; parameters.push(['sort', sort]); parameters.push(['limit', limit]); parameters.push(['radius_filter', radius_filter]); parameters.push(['actionlinks', actionlinks]); parameters.push(['location', address]); parameters.push(['callback', 'callback']); parameters.push(['oauth_consumer_key', auth.consumerKey]); parameters.push(['oauth_consumer_secret', auth.consumerSecret]); parameters.push(['oauth_token', auth.accessToken]); parameters.push(['oauth_signature_method', 'HMAC-SHA1']); var message = { 'action' : 'http://api.yelp.com/v2/search', 'method' : 'GET', 'parameters' : parameters }; OAuth.setTimestampAndNonce(message); OAuth.SignatureMethod.sign(message, accessor); var parameterMap = OAuth.getParameterMap(message.parameters); $.ajax({ url : message.action, cache : true, method : message.method, data : parameterMap, dataType : 'jsonp', jsonp : 'callback', success : resolve, error : reject }); }); }; //View model function MapViewModel(){ var self = this; self.categories = ["Choose option", "Bars", "Gyms"]; var addresses = ["address","address, address", "address","address", "address"]; var yelp = new YelpDataProvider(); self.places = ko.observableArray(); yelp.getDataForPlaces(addresses).then(function(place){ self.places(place); }) } ko.applyBindings(new MapViewModel());
<ul data-bind="foreach: places "> <li data-bind="text: business[0].name"></li> </ul>
There's a bit of a conceptual issue going on here.
If MapViewModel()
is a constructor, it will be called with new
. However, the getLocationDesc()
aspect of the constructor is asynchronous, with the consequence that, as written, new MapViewModel()
will return an object which is effectively still under construction, and with no access to a promise to signify completion of the async process.
Constructors and asynchronism don't mix.
A workaround is to put the async stuff inside a public .getLocationsAsync()
method. Something like this maybe :
function MapViewModel() {
var self = this;
self.categories = ["Choose option", "Bars", "Gyms"];
self.places = ko.observableArray();
var addresses = ["address", "address, address", "address", "address", "address"];
var yelp = new YelpDataProvider();
var locationsPromise = null;// a var in which to cache the promise created by this.getLocationsAsync()
this.getLocationsAsync = function() {
if(!locationsPromise) {
locationsPromise = Promise.all(addresses.map(yelp.getLocationDesc)).then(function(placeDescriptions) {
placeDescriptions.forEach(function(p) {
places.push(p);
});
return places;
});
}
return locationsPromise;
};
}
Probably not 100% correct but hopefully good enough to illustrate the idea.
Now call as follows :
var mapViewModel = new MapViewModel();
mapViewModel.getLocationsAsync().then(function(places) {
// places is an observableArray
});
Note: To be truly useful, you probably want to pass addresses
and categories
to MapViewModel()
, otherwise every instance will be identical. Or maybe MapViewModel()
should be rephrased as a singleton?
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.