该表提交到 AirportController 中的 save 闭包。将清单 8 中的代码添加到控制器,以在保存新的 Airport 之前调用 geocodeAirport:
清单 8. 修改 save 闭包
def results = geocoderService.geocodeAirport(params.iata)
def airport = new Airport(params + results)
if(!airport.hasErrors() && airport.save()) {
flash.message = "Airport ${airport.id} created"
redirect(action:show,id:airport.id)
}
else {
render(view:'create',model:[airport:airport])
}
}
如果在命令提示处输入 grails generate-controller Airport,方法的主要部分将与您所看到的一样。仅仅是开始的两行与默认生成的闭包不同。第一行从 geocoder 服务获得一个 HashMap。第二行将 results HashMap 和 params HashMap 合并起来(当然,在 Groovy 中合并两个 HashMap 就像把它们添加到一起一样简单)。
如果数据库保存成功的话,将重定向到显示操作。幸运的是,不需要更改 show.gsp,如图 3 所示:
图 3. 显示 Airport 表单

要编辑 Airport,必须保持 iata 和 city 字段在 edit.gsp 中不变。您可以从 show.gsp 复制和粘贴其余的字段,把它们变为只读字段(或者,如果您能从 前期文章 体会到 “复制和粘贴是面向对象编程的最低级形式” 的话,您可以把常用字段提取到一个局部模板并在 show.gsp 和 edit.gsp 中呈现它)。清单 9 展示了修改后的 edit.gsp:
清单 9. 修改 edit.gsp
<input type="hidden" name="id" value="${airport?.id}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="iata">Iata:</label></td>
<td valign="top"
class="value ${hasErrors(bean:airport,field:'iata','errors')}">
<input type="text"
maxlength="3"
id="iata"
name="iata"
value="${fieldValue(bean:airport,field:'iata')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="city">City:</label></td>
<td valign="top"
class="value ${hasErrors(bean:airport,field:'city','errors')}">
<input type="text"
id="city"
name="city"
value="${fieldValue(bean:airport,field:'city')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Name:</td>
<td valign="top" class="value">${airport.name}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">State:</td>
<td valign="top" class="value">${airport.state}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Country:</td>
<td valign="top" class="value">${airport.country}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Lat:</td>
<td valign="top" class="value">${airport.lat}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Lng:</td>
<td valign="top" class="value">${airport.lng}</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" value="Update" /></span>
<span class="button">
<g:actionSubmit class="delete"
onclick="return confirm('Are you sure?');"
value="Delete" />
</span>
</div>
</g:form>
图 4. 编辑 Airport 表单

单击 Update 按钮将表单值发送到 update 闭包。将服务调用和 hashmap 合并添加到默认代码,如清单 10 所示:
清单 10. 修改 update 闭包
def airport = Airport.get( params.id )
if(airport) {
def results = geocoderService.geocodeAirport(params.iata)
airport.properties = params + results
if(!airport.hasErrors() && airport.save()) {
flash.message = "Airport ${params.id} updated"
redirect(action:show,id:airport.id)
}
else {
render(view:'edit',model:[airport:airport])
}
}
else {
flash.message = "Airport not found with id ${params.id}"
redirect(action:edit,id:params.id)
}
}
到目前为止,您已经像 Google Map 一样无缝地将地理编码集成到您的应用程序里。花点时间想一想在应用程序中捕获地址的所有位置 — 顾客、雇员、远程办公室、仓库和零售点等等。通过简单地添加几个字段以存储纬度/经度坐标和加入一个地理编码服务,就能够设置一些简易的地图来显示对象 — 这正是我下一步的工作。