Pregunta

I have created a Knockout binding to be able to toggle KML layers with Google Maps, but the solution seems a bit slow and "flickerish". How can I avoid re-creating the map and layers on each toggle?

A running demo can be found here

var ViewModel = function () {
    var self = this;

    self.mapOptions = {
        center: new google.maps.LatLng(60.390791, 5.306396),
        zoom: 2
    };

    self.levels = [{
        text: "Type 1",
        countries: ko.observableArray([
            'https://dl.dropbox.com/u/2873968/countries-kml/afghanistan.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/algeria.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/bahrain.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/burundi.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/ca_republic.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/cameroon.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/chad.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/colombia.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/dr_congo.kml']),
        isVisible: ko.observable(false)
    }, {
        text: "Type 2",
        countries: ko.observableArray([
            'https://dl.dropbox.com/u/2873968/countries-kml/russia.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/sudan.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/syria.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/thailand.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/venezuela.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/yemen.kml',
            'https://dl.dropbox.com/u/2873968/countries-kml/zimbabwe.kml']),
        isVisible: ko.observable(true)
    }];
};

ko.bindingHandlers.KML = {
    update: function (element, valueAccessor) {
        var data = ko.utils.unwrapObservable(valueAccessor()),
            mapOptions = ko.utils.unwrapObservable(data.mapOptions) || {},
            levels = ko.utils.unwrapObservable(data.levels) || [],
            map = new google.maps.Map(element, mapOptions);

        for (var i = 0; i < levels.length; i++) {
            var level = levels[i],
                isVisible = level.isVisible(),
                text = level.text,
                countries = ko.utils.unwrapObservable(level.countries) || [];

            for (var y = 0; y < countries.length; y++) {
                var country = countries[y],
                    layer = new google.maps.KmlLayer(country, {
                        map: map,
                        preserveViewport: true
                    });

                if (isVisible) {
                    layer.setMap(map);
                } else {
                    layer.setMap(null);
                }

            }
        }
    }
};

ko.applyBindings(new ViewModel());
¿Fue útil?

Solución

First thing to do is at least use the init callback.

ko.bindingHandlers.KML = {
    init: function (element, valueAccessor) {
        var data = ko.utils.unwrapObservable(valueAccessor()),
            mapOptions = ko.utils.unwrapObservable(data.mapOptions) || {},
            levels = ko.utils.unwrapObservable(data.levels) || [],
            map = new google.maps.Map(element, mapOptions);

        // now that the map is created, create layers for each level in each country

        // set the layers visibility

    }
}

Then, in the update callback you only need to update the visibility of the layers

ko.bindingHandlers.KML = {
    init: function (element, valueAccessor) {

    },
    update: function(element, valueAccessor){
        // get data from valueAccessor

        // for each level, set visibility
    }
}

But, now we don't have a map to use in the update callback. Fortunately we can create our own objects inside our bindingHanlder, so let's do that:

ko.bindingHandlers.KML = {
    config : {
       map: {}  
    }, 
    init: function (element, valueAccessor) {
        var map = new google.maps.Map(element, mapOptions);

        // now we can store our map;
        ko.bindingHandlers.KML.config.map = map;

    },
    update: function(element, valueAccessor){
        // and use it in the update
        var map ko.bindingHandlers.KML.config.map;
    }
}

This also means we can define a model for our layer as well and let that model control visibility through an observable.

This all results in the following jsFiddle example

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top