简体   繁体   中英

How to bind array of objects from Promise Knockout JS

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> 

complex object: 在此处输入图片说明

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.

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