Yandex 地图 API 2.1。使用 knockoutjs 动态过滤地标

Yandex maps API 2.1. Filter placemarks dynamically using knockoutjs

我有以下问题:

我有几个参数,例如国家、地区、城市、品牌和名称以及 500 多个 yandex 地图地标,它们是通过 ObjectManager (Yandex API 2.0) 添加的。我需要按参数过滤地标,但是当我按下 'Search' 按钮时,只有一个地标隐藏。请帮助我了解我做错了什么。谢谢

代码片段如下:

var dataFromServer = {
    directions: [
        {id: 1, name: 'Commercial'}, 
        {id: 2, name: 'Agriculture'}, 
        {id: 3, name: 'Building'}
    ],
    brands: [
        {id: 1, name: 'Iveco'}, 
        {id: 2, name: 'New Holland Agriculture'}, 
        {id: 3, name: 'Case Agriculture'}
    ],
    countries: [
        {id: 1, name: 'Russia'}, 
        {id: 2, name: 'USA'}, 
        {id: 3, name: 'Germany'}
    ],
    regions: [
        {id: 1, country: 1, name: 'Russia region 1'},
        {id: 2, country: 1, name: 'Russia region 2'},
        
        {id: 3, country: 2, name: 'USA region 1'},
        {id: 4, country: 2, name: 'USA region 2'},
        
        {id: 5, country: 3, name: 'Germany region 1'},
        {id: 6, country: 3, name: 'Germany region 2'}      
    ],
    cities: [
        {id: 1, region: 1, name: 'Russia region 1 city 1'},
        {id: 2, region: 1, name: 'Russia region 1 city 2'},
        {id: 3, region: 2, name: 'Russia region 2 city 1'},
        {id: 4, region: 2, name: 'Russia  region 2 city 2'},
        
        {id: 5, region: 3, name: 'USA region 1 city 1'},
        {id: 6, region: 3, name: 'USA region 1 city 2'},
        {id: 7, region: 4, name: 'USA region 2 city 1'},
        {id: 8, region: 4, name: 'USA region 2 city 2'},
        
        {id: 9, region: 5, name: 'Germany region 1 city 1'},
        {id: 10, region: 5, name: 'Germany region 1 city 2'},
        {id: 11, region: 6, name: 'Germany region 2 city 1'},
        {id: 12, region: 6, name: 'Germany region 2 city 2'}        
    ]
},
    testDealers = {
      type: "FeatureCollection",
      features: [
            {
                type: 'Feature',
                geometry: {
                    type: 'Point',
                    coordinates: [55.34954, 37.721587]
                },
                options: {
                    brand: 1,
                    direction: 1,
                    city: 1,
                    name: "Test"
                }
            },
            {
                type: 'Feature',
                geometry: {
                    type: 'Point',
                    coordinates: [55.24954, 37.4]
                },
                options: {
                    brand: 2,
                    direction: 2,
                    city: 2,
                    name: "Test 2"
                }
            },
            {
                type: 'Feature',
                geometry: {
                    type: 'Point',
                    coordinates: [55.14954, 37.61587] 
                },
                options: {
                    brand: 3,
                    direction: 1,
                    city: 3,
                    name: "Test 3"
                }
            }
      ]
    };

// end testing data

var ViewModel = ko.mapping.fromJS(dataFromServer);

ViewModel.country = ko.observable();
ViewModel.region = ko.observable();
ViewModel.city = ko.observable();
ViewModel.brand = ko.observable();
ViewModel.direction = ko.observable();
ViewModel.name = ko.observable();

ViewModel.computedRegions = ko.computed(function () {
    return ko.utils.arrayFilter(ViewModel.regions(), function(item) {
        return item.country() == ViewModel.country();
    });
});

ViewModel.computedCities = ko.computed(function () {
    return ko.utils.arrayFilter(ViewModel.cities(), function(item) {
        return item.region() == ViewModel.region();
    });
});

ViewModel.searchQuery = ko.computed(function(){
    return ko.toJSON({
        country: ViewModel.country(),
        region: ViewModel.region(),
        direction: ViewModel.direction(),
        city: ViewModel.city(),
        brand: ViewModel.brand(),
        name: ViewModel.name()
    });
});

ViewModel.busy = ko.observable(false);

ViewModel.search = function(){
    window.objectManager.setFilter("options.brand == " + ViewModel.brand());
}

ymaps.ready(function(){   
    window.dealermap = new ymaps.Map('dealermap', {
        center: [55.30954, 37.721587],
        zoom: 8
    });    
    
    window.objectManager = new ymaps.ObjectManager({ clusterize: true });
    window.objectManager.add(ko.toJSON(testDealers));
    window.dealermap.geoObjects.add(window.objectManager);
    window.dealermap.setBounds(objectManager.getBounds());
    
    ko.applyBindings(ViewModel);
});
#dealermap { width: 300px; height: 300px; padding: 0; margin: 0; border: 1px solid #000}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>

<p>Direction:
    <select data-bind="options: directions,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: direction,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Country:
    <select data-bind="options: countries,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: country,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Region:
    <select data-bind="options: computedRegions,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: region,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>City:
    <select data-bind="options: computedCities,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: city,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Brand:
    <select data-bind="options: brands,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: brand,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Name:
    <input type="text" data-bind="value: name, disable: busy" />
</p>
<p>
    <button data-bind="click: search, disable: busy">Search</button>
</p>

<script type="text/html" id="dealer-template">
    <li><a target="_blank" data-bind="attr: { href: link }, text: name"></a></li>
</script>

<hr />
<p>
    Search query:
    <div data-bind="text: searchQuery"></div>
</p>

<div data-bind="if: busy"><img src="http://ukrainianwall.com/wp-content/themes/legatus-theme/images/loading.gif"/></div>

<div data-bind="visible: !busy()">    
    Map:        
    <div id="dealermap"></div>
</div>

FeatureCollection 中的所有对象都必须有一个唯一的 ID 并且过滤器应该是一个函数(而不是字符串)以获得更好的结果。以下是按 BRAND ONLY 过滤的示例。其他过滤器将相同:

var dataFromServer = {
    directions: [
        {id: 1, name: 'Commercial'}, 
        {id: 2, name: 'Agriculture'}, 
        {id: 3, name: 'Building'}
    ],
    brands: [
        {id: 1, name: 'Iveco'}, 
        {id: 2, name: 'New Holland Agriculture'}, 
        {id: 3, name: 'Case Agriculture'}
    ],
    countries: [
        {id: 1, name: 'Russia'}, 
        {id: 2, name: 'USA'}, 
        {id: 3, name: 'Germany'}
    ],
    regions: [
        {id: 1, country: 1, name: 'Russia region 1'},
        {id: 2, country: 1, name: 'Russia region 2'},
        
        {id: 3, country: 2, name: 'USA region 1'},
        {id: 4, country: 2, name: 'USA region 2'},
        
        {id: 5, country: 3, name: 'Germany region 1'},
        {id: 6, country: 3, name: 'Germany region 2'}      
    ],
    cities: [
        {id: 1, region: 1, name: 'Russia region 1 city 1'},
        {id: 2, region: 1, name: 'Russia region 1 city 2'},
        {id: 3, region: 2, name: 'Russia region 2 city 1'},
        {id: 4, region: 2, name: 'Russia  region 2 city 2'},
        
        {id: 5, region: 3, name: 'USA region 1 city 1'},
        {id: 6, region: 3, name: 'USA region 1 city 2'},
        {id: 7, region: 4, name: 'USA region 2 city 1'},
        {id: 8, region: 4, name: 'USA region 2 city 2'},
        
        {id: 9, region: 5, name: 'Germany region 1 city 1'},
        {id: 10, region: 5, name: 'Germany region 1 city 2'},
        {id: 11, region: 6, name: 'Germany region 2 city 1'},
        {id: 12, region: 6, name: 'Germany region 2 city 2'}        
    ]
},
    testDealers = {
      type: "FeatureCollection",
      features: [
            {
                type: 'Feature',
                id: 0,
                geometry: {
                    type: 'Point',
                    coordinates: [55.34954, 37.721587]
                },
                options: {
                    brand: 1,
                    direction: 1,
                    city: 1,
                    name: "Test"
                }
            },
            {
                type: 'Feature',
                id: 1,
                geometry: {
                    type: 'Point',
                    coordinates: [55.24954, 37.4]
                },
                options: {
                    brand: 2,
                    direction: 2,
                    city: 2,
                    name: "Test 2"
                }
            },
            {
                type: 'Feature',
                id: 2,
                geometry: {
                    type: 'Point',
                    coordinates: [55.14954, 37.61587] 
                },
                options: {
                    brand: 3,
                    direction: 1,
                    city: 3,
                    name: "Test 3"
                }
            }
      ]
    };

// end testing data

var ViewModel = ko.mapping.fromJS(dataFromServer);

ViewModel.country = ko.observable();
ViewModel.region = ko.observable();
ViewModel.city = ko.observable();
ViewModel.brand = ko.observable();
ViewModel.direction = ko.observable();
ViewModel.name = ko.observable();

ViewModel.computedRegions = ko.computed(function () {
    return ko.utils.arrayFilter(ViewModel.regions(), function(item) {
        return item.country() == ViewModel.country();
    });
});

ViewModel.computedCities = ko.computed(function () {
    return ko.utils.arrayFilter(ViewModel.cities(), function(item) {
        return item.region() == ViewModel.region();
    });
});

ViewModel.searchQuery = ko.computed(function(){
    return ko.toJSON({
        country: ViewModel.country(),
        region: ViewModel.region(),
        direction: ViewModel.direction(),
        city: ViewModel.city(),
        brand: ViewModel.brand(),
        name: ViewModel.name()
    });
});

ViewModel.busy = ko.observable(false);

ViewModel.search = function(){
    window.objectManager.setFilter(function(object){
        return object.options.brand == ViewModel.brand();
    });
    window.dealermap.setBounds(objectManager.getBounds());
}

ymaps.ready(function(){   
    window.dealermap = new ymaps.Map('dealermap', {
        center: [55.30954, 37.721587],
        zoom: 8
    }, { maxZoom: 15, minZoom: 3 });    
    
    window.objectManager = new ymaps.ObjectManager({ clusterize: true });
    window.objectManager.add(ko.toJSON(testDealers));
    window.dealermap.geoObjects.add(window.objectManager);
    window.dealermap.setBounds(objectManager.getBounds());
    
    ko.applyBindings(ViewModel);
});
#dealermap { width: 300px; height: 300px; padding: 0; margin: 0; border: 1px solid #000}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>

<p>Direction:
    <select data-bind="options: directions,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: direction,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Country:
    <select data-bind="options: countries,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: country,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Region:
    <select data-bind="options: computedRegions,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: region,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>City:
    <select data-bind="options: computedCities,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: city,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Brand:
    <select data-bind="options: brands,
                       disable: busy,
                       optionsText: 'name',
                       optionsValue: 'id',
                       value: brand,
                       optionsCaption: 'Not selected...'">
    </select>
</p>
<p>Name:
    <input type="text" data-bind="value: name, disable: busy" />
</p>
<p>
    <button data-bind="click: search, disable: busy">Search</button>
</p>

<script type="text/html" id="dealer-template">
    <li><a target="_blank" data-bind="attr: { href: link }, text: name"></a></li>
</script>

<hr />
<p>
    Search query:
    <div data-bind="text: searchQuery"></div>
</p>

<div data-bind="if: busy"><img src="http://ukrainianwall.com/wp-content/themes/legatus-theme/images/loading.gif"/></div>

<div data-bind="visible: !busy()">    
    Map:        
    <div id="dealermap"></div>
</div>