目录
概述
- 基于Ionic框架(Angular)开发hybrid移动应用,主要功能有地图要素展示、定位、导航、文件上传。
- 用户可查看或上传野生动物受伤地点,以及查看用户周围的援助设施(如警局、医院等)并查询路线
- 前端:HTML, Typescript结合Ionic框架(Angular),利用Google Map API对用户位置进行定位以及导航
- 后端:php, MySQL数据库
代码
app.module.ts
在每个子页面ts中import的module都要在app.module.ts中import并写入@NgModule中
import { FormsModule,ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [AppComponent,SettingModalPage,ModalMapPage],
entryComponents: [SettingModalPage,ModalMapPage],
imports: [
FormsModule,
ReactiveFormsModule],
})
页面路由
- app-routing.module.ts中的Routes对象(Route对象被Angular抽象成一个接口,多个Route对象组成的数组用Route表示,注意跟Router: class区分)定义了路由的路径属性。
用ionic generate page生成一个新的页面,就会在这里自动新增一个Route对象。
- app.component.html中的<ion-router-outlet>标签是页面中路由的出口。Router动态创建对应的组件,插入到<ion-router-outlet>标签中

- 导航栏是tabs文件夹下的内容
tabs.router.module.ts的Routes对象指定tabs导航栏怎么跳转到其他页面。所有子页面的Route对象都是tabs这一Route对象的子对象,即在其children数组内。
用ionic generate page生成一个新的页面,要在这里创建对应的Route对象,即完成了一个页面的路由导航
tabs.page.html是导航栏的布局模板,在这里修改页面中标签的名称

创建search页面的地理定位(tab2)
关键:
- 调用geolocation插件,获取定位
- 函数的异步调用
- 插值{{}}的使用
调用geolocation插件,获取定位
- 安装插件


- import模块
src/app/app.module.ts.中
- 在子页面ts文件中import地理模块,在类的构造函数中声明geolocation

- 在类中声明变量

函数的异步调用
在类中声明方法findCurrentLocation(),用于获取定位。里面的then是异步调用,如果position成功返回则调用第一个箭头后的,如果失败则返回error,调用第二个箭头后的。

在类的constructor中调用findCurrentLocation()

插值{{}}的使用
- 在子页面的模板中(
src/app/tab2/tab2.page.html)用插值({{}})把ts中生成的值绑定在页面
- 最后得到页面

创建定位地图页(map)
关键:
- 导入Router模块,导航到另一个页面和传参
- 使用ngOnInit()定义初始化函数
- 调用google map api创建地图
- 生成地图页

导入Router服务,导航到另一个页面和传参
import中导入,并在constructor中进行依赖注入Router服务

在tab2.page.ts中定义页面跳转函数goToMapPage(),在函数内部先调用findCurrentLocation()来获取当前坐标。

在goToMapPage()调用**router.navigate()**方法导航到地图页面,添加一个 NavigationExtras (interface)对象,设置queryParams参数,并把这个页面获取到的坐标经纬度传给地图页面。

在src/tab2/tab2.page.html中创建按钮绑定事件函数goToMapPage()

在src/index.html中插入Google Maps JavaScript API的脚本,用于地图页面调用google的api

在src/app/map/map.page.html中创建地图画布,src/app/map/map.page.scss中设置画布样式


src/pages/map/map.page.ts中声明变量google

在类中声明变量,其中变量mapElement来自html,用@ViewChild(属性装饰器,用于配置一个视图查询,类似getElementById)获取,设为ElementRef类(对视图中某个原生元素的包装器。)。

map.page.ts中导入,并注入依赖的服务ActivatedRoute(包含与当前组件相关的路由信息。)

使用ngOnInit()定义初始化函数
- 用ActivatedRoute对象(interface)的queryParams参数获取tab2传来的参数。参数在传输过程转为字符串,要用**Number()**转为数值。
**ngOnInit()**中设置表示组件一初始化就获取参数。
调用google map api创建地图
- 定义loadMap()函数,用于调用Google API的google.maps.LatLng生成地图坐标,google.maps.Map(mapElement[, opts]) 创建地图,google.maps.Marker([opts])生成弹出窗口

创建google.maps.Geocoder对象,用 geocode(request, callback) 方法把坐标转为地址(reverse geocode),显示在弹出窗口
- ngOnInit()中调用loadMap(),表示一初始化完成就生成地图

- 最后结果(点击得到弹窗)

创建搜索路径页面(setting-modal)
用NavParams, ModalController在页面内的窗口传参
tab2.page.ts中import导入,并在constructor依赖注入ModalController (Ionic的内容窗格类,在本页面生成的一个窗口,通常用于做选项设置)
异步创建一个modalController窗口,在关闭时获取子页面的数据

setting-modal.page.ts中import导入,并在constructor依赖注入


用*ngFor指令循环写数据
[{ngModel}]双向绑定实现输入和脚本同步
setting-modal.page.html
创建places页面,用于寻找周围设施
1. 用Promise的异步执行
2. 用Google Map的PlacesService查找地点
places.page.ts
ngOnInit() {
// 从tab2.ts传入数据
this.route.queryParams.subscribe(params =>{
console.log("param "+params);
console.log(params.latitude);
this.latitude = parseFloat(params.latitude);
this.longitude = parseFloat(params.longitude);
this.searchType = params.queryType;
this.keyword = params.queryName;
this.distance = parseInt(params.distance);
console.log("distance "+params.distance);
console.log("longitude "+params.longitude);
});
// 1.2 查找地方,resolve则循环搜索结果,在地图上mark出来;reject则输出错误
this.queryPlaces().then((results:Array<any>)=>{
for(let i=0;i<results.length;i++){
this.createMarker(results[i]);
}
this.places = results;
},(status)=>console.log(status));
}
queryPlaces(){
// 2.1 创建地图
this.currentLocation = new google.maps.LatLng(this.latitude,this.longitude);
let mapOptions = {
center:this.currentLocation,
zoom:11
}
this.map = new google.maps.Map(document.getElementById('map'),mapOptions);
// 2.2 创建google map的PlacesService对象,用于查找一定范围区域的searchType类型
this.service = new google.maps.places.PlacesService(this.map);
this.request = {
location: this.currentLocation,
radius: this.distance,
type: this.searchType,
rankBy: google.maps.places.DISTANCE
};
// 1.1 创建Promise对象,先执行nearbySearch再展示(resolve的话),或返回status(reject的话)
return new Promise((resolve, reject)=>{
this.service.nearbySearch(this.request,function(result,status){
if(status === google.maps.places.PlacesServiceStatus.OK){
resolve(result);
}
else{
reject(status);
}
});
});
}
// 2.3 用Marker对象创建标记点
createMarker(place){
let marker = new google.maps.Marker({
map:this.map,
animation:google.maps.Animation.DROP,
position:place.geometry.location,
title:place.name+place.vicinity+place.rating,
});
google.maps.event.addListener(marker,'click',function(){
let infowindow = new google.maps.InfoWindow({
content:marker.title
});
infowindow.open(this.map,marker)
});
}
创建direction导航页面
用*ngFor指令循环插值和事件绑定
places.page.html
<ion-list>
<ion-item>
{{places?.length}} PLACES FOUND!<br/>
</ion-item>
<ion-item *ngFor="let place of places; let i = index" (click)="goToDirectionPage(i)">
<ion-label>
{{place.name}}<br/> {{place.vicinity}} <br/> Average Rating:{{place.rating}}
</ion-label>
</ion-item>
</ion-list>
places.page.ts
// 导航到direction页面,在html中点击调用,用index表示循环中的第几个place
goToDirectionPage(index){
// 把数据传给direction页面,必须转为字符串类型
let navigationExtras:NavigationExtras = {
queryParams:{
origin:JSON.stringify(this.currentLocation),
destination:JSON.stringify(this.places[index].geometry.location),
}
};
this.router.navigate(['direction'],navigationExtras);
}
用Google Map的DirectionsService和DirectionsRenderer对象设置导航
ngOnInit() {
// 获取从places页面传来的数据,把字符串类型转回对象
this.route.queryParams.subscribe(params =>{
console.log("param "+params);
this.origin = params.origin;
this.destination = JSON.parse(params.destination);
this.originObject = JSON.parse(this.origin);
console.log("origin "+params.origin);
console.log("destination "+params.destination);
});
//
this.calculateRoute();
}
calculateRoute(){
let mapOptions = {
center:new google.maps.LatLng(this.originObject),
zoom:11
}
this.map = new google.maps.Map(document.getElementById('directionMap'),mapOptions);
// 创建导航对象
let directionsService = new google.maps.DirectionsService();
let directionsDisplay = new google.maps.DirectionsRenderer();
directionsDisplay.setMap(this.map);
let request = {
origin:this.originObject,
destination:this.destination,
travelMode: 'DRIVING'
}
directionsService.route(request,function(response,status){
if (status === google.maps.DirectionsStatus.OK){
directionsDisplay.setDirections(response);
directionsDisplay.setPanel(document.getElementById('directionPanel'));
console.log("status: " +status)
var bounds = new google.maps.LatLngBounds();
bounds.extend(this.originObject);
bounds.extend(this.destination);
this.map.fitBounds(bounds);
}
else{
window.alert('Directions request failed due to '+status);
}
});
}
创建表单页面(tab3)
用JSON.stringfy把输出的[object, object]转为字符串
用正则表达式去字符串的双引号
async openModalMap(){
const ModalMap = await this.modalController.create({
component: ModalMapPage,
componentProps:{latitude:this.latitude, longitude:this.longitude, address:this.address}
});
console.log("lati "+this.latitude)
console.log("long "+this.longitude)
// 关闭子页面setting-modal时,主页面获取子页面的数据searchDistance
ModalMap.onDidDismiss().then((address)=>{
// 正则表达式去字符串的双引号
// JSON.stringfy把输出的[object, object]转为字符串
this.address = JSON.stringify(address.data).replace(/"/g,"")
this.form.controls.address.setValue(this.address);
console.log('address: '+ JSON.stringify(address.data));
});