Ember组件功能咨询:基于地理位置的兴趣点排序问题
嘿,我看了你当前的代码,发现问题出在距离计算的架构上——每个miles-away组件各自单独计算对应酒庄的距离,这就导致你在控制器或路由层面拿不到所有酒庄的距离数据,自然没法对整个列表进行排序。咱们换个思路,把距离计算移到控制器里批量处理,这样就能一次性获取所有数据来排序了。
核心思路调整
把原本分散在组件里的距离计算逻辑,集中到控制器中批量调用Google Maps的DistanceMatrixService,这样能一次性拿到所有酒庄的距离数据,之后就可以轻松对列表进行排序。同时简化miles-away组件,让它只负责显示已计算好的距离。
具体代码修改
1. 更新控制器(处理批量计算与排序)
// controllers/vineyard/index.js import Ember from 'ember'; import { inject as service } from '@ember/service'; import { later } from '@ember/runloop'; export default Ember.Controller.extend({ userLocation: null, locationIsLoading: true, failState: false, googleMapsApi: service(), geolocation: service(), panelActions: Ember.inject.service(), // 自动根据距离排序的酒庄列表 sortedVineyards: Ember.computed('userLocation', 'vineyards.@each.distance', function() { // 过滤出能计算出距离的酒庄,按距离升序排列(最近的在前) return this.get('vineyards') .filter(vineyard => vineyard.get('distance') !== Infinity) .sortBy('distance'); }), userLocationChanged: function () { this.toggleProperty('locationIsLoading'); }.observes('userLocation'), actions: { distanceFrom: function() { this.get('geolocation').trackLocation().then((geoObject) => { let currentLocation = this.get('geolocation.currentLocation'); this.set('userLocation', currentLocation); // 触发批量距离计算 this._calculateAllDistances(currentLocation); }, (reason) => { this.set('failState', true); console.log('Geolocation failed because ' + reason); }); }, stopError: function() { this.set('failState', false); $('.error').css('height', '0'); $('.location-loader').animate({opacity: '0'}, 1000); } }, // 私有方法:批量计算所有酒庄的距离 _calculateAllDistances(userLocation) { if (!userLocation || !this.get('vineyards.length')) return; const origin = `${userLocation[0]},${userLocation[1]}`; const destinations = this.get('vineyards').map(vineyard => vineyard.get('location')); this.get('googleMapsApi.google').then((google) => { const distanceMatrixService = new google.maps.DistanceMatrixService(); distanceMatrixService.getDistanceMatrix({ origins: [origin], destinations: destinations, travelMode: google.maps.TravelMode.DRIVING, unitSystem: google.maps.UnitSystem.IMPERIAL, avoidHighways: false, avoidTolls: false }, (response, status) => { if (status !== google.maps.DistanceMatrixStatus.OK) { this.set('failState', true); return; } // 遍历结果,给每个酒庄添加距离数据 const rows = response.rows[0].elements; this.get('vineyards').forEach((vineyard, index) => { const element = rows[index]; if (element.status === "OK") { // 保存数值型距离(用于排序)和文本型距离(用于显示) vineyard.set('distance', element.distance.value); vineyard.set('distanceText', element.distance.text); } else { // 无法计算距离的酒庄,放到排序最后 vineyard.set('distance', Infinity); vineyard.set('distanceText', 'N/A'); } }); // 计算完成后更新加载状态 later(() => { this.set('locationIsLoading', false); }, 45); }); }); } });
2. 简化miles-away组件
现在组件只需要负责显示已计算好的距离:
// components/miles-away.js import Component from '@ember/component'; export default Component.extend({ // 接收父组件传递的距离文本 distanceText: null });
组件模板也简化:
{{!-- components/miles-away.hbs --}} <div class="miles-away-container"> <p class="miles-away fade-in">{{distanceText}}</p> </div> {{yield}}
3. 更新模板渲染排序后的列表
{{!-- templates/vineyard/index.hbs --}} <div class="distance"> {{#if failState}} {{svg-jar 'fail'}} {{/if}} {{#if locationIsLoading}} {{location-loader}} {{else}} {{#each sortedVineyards as |vineyard|}} <!-- 这里替换成你的酒庄卡片结构 --> <div class="vineyard-item"> <h3>{{vineyard.name}}</h3> <p>{{vineyard.location}}</p> {{miles-away distanceText=vineyard.distanceText}} </div> {{/each}} {{/if}} </div>
额外说明
- 效率提升:批量调用
DistanceMatrixService比单个组件调用更高效,减少了网络请求次数,也更符合Google Maps API的使用规范。 - 模型属性:如果你的
vineyard是Ember Data模型,不需要为distance和distanceText添加持久化字段,直接用set临时设置即可,因为这是计算出来的非持久化数据。 - 异常处理:对无法计算距离的酒庄做了特殊处理,会把它们放到排序列表的最后,避免影响正常显示。
内容的提问来源于stack exchange,提问作者Chris Johnson
相关产品推荐
相关产品推荐

