PrimeFaces Gmap无整体刷新绘制多边形的实现方法
PrimeFaces Gmap 点击绘制多边形不重载组件实现方案
问题背景
使用JSF + PrimeFaces开发集成Gmap组件的应用时,需要实现两个核心需求:
- 用户点击地图选取顶点即可实时绘制多边形,每次点击后能即时看到多边形构建效果
- 所有选中的点位数据需要同步到Backing Bean,支持后续持久化存储到数据库
最初按照「每次点击后同步更新地图与Backing Bean模型」的逻辑实现,在<p:ajax>标签中配置了update="@this",但该配置会触发整个Gmap组件完全重载,导致当前地图的平移位置、缩放级别、中心点参数全部重置为初始值,用户每次点击后都要重新缩放、平移回目标绘制区域,操作体验极差。
已知要实现无整体重载的更新需要结合JavaScript,以下是最初的最小复现代码。
初始复现代码
技术栈版本
- JSF 2.3(Mojarra实现)
- PrimeFaces 10.0
- Wildfly 26.1
JSF页面初始代码
<h:head> <script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=true"></script> </h:head> <h:body> <h:form id="frmMap2"> <p:gmap center="41.381542, 2.122893" zoom="15" type="HYBRID" style="width:100%;height:400px" model="#{testMapBean.polygonModel}"> <p:ajax event="pointSelect" listener="#{testMapBean.onPointSelect}" update="@this"/> </p:gmap> </h:form> </h:body>
Backing Bean初始代码(ViewScoped作用域)
@Named(value = "testMapBean") @ViewScoped public class TestMapBean implements Serializable { private MapModel polygonModel; private Polygon polygon; public TestMapBean() { } @PostConstruct public void init() { polygonModel = new DefaultMapModel(); polygon = new Polygon(); polygon.setStrokeColor("#FF9900"); polygon.setFillColor("#FF9900"); polygon.setStrokeOpacity(0.7); polygon.setFillOpacity(0.7); polygonModel.addOverlay(polygon); } public void onPointSelect(PointSelectEvent event) { LatLng latlng = event.getLatLng(); polygon.getPaths().add(latlng); } // 省略公共getter与setter方法 }
最终实现方案
核心思路是彻底移除update="@this"配置,从根源上避免整个Gmap组件重载:前端提前初始化Google Maps原生多边形实例,用户点击选点后仅将坐标传到后端存储,后端拼接JS调用语句直接更新前端原生多边形的路径,全程不触发组件重渲染,地图的缩放级别、平移位置、中心点状态会完全保留。
视图层完整代码
<h:head> <script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=true"></script> <script type="application/javascript"> var map; var pl; function initbs() { // 通过PrimeFaces widget变量获取地图原生实例 map = PF('wmaptest').map; // 初始化原生谷歌地图多边形实例 pl = new google.maps.Polygon({ strokeColor: "#777777", strokeOpacity: 0.8, strokeWeight: 2, fillColor: "#FF0000", fillOpacity: 0.35, }); } // 更新多边形路径的方法 function drline(coords) { // 将多边形挂载到地图,设置最新坐标路径 pl.setMap(map); pl.setPath(coords); } </script> </h:head> <h:body> <h:form id="frmMap2"> <p:gmap widgetVar="wmaptest" center="41.381542, 2.122893" zoom="15" type="ROADMAP" style="width:100%;height:800px"> <p:ajax event="pointSelect" listener="#{testMapBean.onPointSelect}"/> </p:gmap> </h:form> </h:body>
后端Bean完整代码
@Named(value = "testMapBean") @ViewScoped public class TestMapBean implements Serializable { // 存储所有选中的多边形顶点 private Set<LatLng> latLngList = new HashSet<LatLng>(); public TestMapBean() { } @PostConstruct public void init() { // 页面初始化完成后执行JS初始化方法 PrimeFaces.current().executeInitScript("initbs()"); } /** * 拼接JS调用语句,触发前端多边形更新 */ public void drawLine() { String scriptStr = latLngList.stream() .map(this::toJavaScriptCoord) .collect(Collectors.joining(",", "drline([", "])")); PrimeFaces.current().executeScript(scriptStr); } /** * 将后端LatLng对象转为JS可识别的坐标对象字符串 */ private String toJavaScriptCoord(LatLng latLng) { return "{lat:" + latLng.getLat() + ",lng:" + latLng.getLng() + "}"; } /** * 地图选点事件监听 */ public void onPointSelect(PointSelectEvent event) { LatLng latlng = event.getLatLng(); latLngList.add(latlng); // 选点完成后触发前端多边形更新 drawLine(); } // 公共getter与setter public Set<LatLng> getLatLngList() { return latLngList; } public void setLatLngList(Set<LatLng> latLngList) { this.latLngList = latLngList; } }
内容的提问来源于stack exchange,提问作者Leandro Alsina
相关产品推荐
相关产品推荐

