第六课:保存和取回数据

如果你认真学过之前的应用制作的话,那么制作一个Data服务对你来说相当简单了。这个跟之前的有一点点不同,因为之前的数据服务都是存储一套数据,但是这个应用中我们要存储很多数据:

  • 营地坐标
  • 营地细节
  • 我的细节

但是理念还是差不多的,看起来可能会稍有不同。我们已经有了自己的表单并且我们的地图页也向咱们的Data服务发送数据了,所以我们要做的是保存它们即可,同时我们需要稍做变更来加载数据回应用。我们先从实现Data服务开始。
> 修改src/providers/data.ts 为如下:

  1. import { Storage } from '@ionic/storage';
  2. import { Injectable } from '@angular/core';
  3. @Injectable()
  4. export class Data {
  5. constructor(public storage: Storage){
  6. }
  7. setMyDetails(data: Object): void {
  8. let newData = JSON.stringify(data);
  9. this.storage.set('mydetails', newData);
  10. }
  11. setCampDetails(data: Object): void {
  12. let newData = JSON.stringify(data);
  13. this.storage.set('campdetails', newData);
  14. }
  15. setLocation(data: Object): void {
  16. let newData = JSON.stringify(data);
  17. this.storage.set('location', newData);
  18. }
  19. getMyDetails(): Promise<any> {
  20. return this.storage.get('mydetails');
  21. }
  22. getCampDetails(): Promise<any> {
  23. return this.storage.get('campdetails');
  24. }
  25. getLocation(): Promise<any> {
  26. return this.storage.get('location');
  27. }
  28. }

Storage是Ionic的通用存储服务,他负责使用最佳存储方案的同时给我们提供了统一的API。
当运行在设备上的时候,如果SQLite插件可用(早先安装过了),他会使用本机的SQLite数据库来存储数据。由于SQLite只会在应用运行在真机上才可用,Storage在SQLite不可用的情况下用IndexedDBWebSQL,或者是浏览器的localStorage
应当尽量使用SQLite,因为基于浏览器的本地存储不怎么可靠,可以被操作系统随机清理掉。自己的数据存在被随机清理掉明显体验很不好。
在其他应用中,我们基本只有两个函数,一个获取数据一个存储数据。在本应用中,由于我们涉及到三套数据,所以我们创建三个set函数和三个get很熟。set方法接受数据传入然后对传入的数据进行存储(在将他转换成一个JSON字符串之后),get负责将这些数据从数据库中获取回来。get函数将返回一个用于resolve返回数据的promise,而不是直接返回数据,所以我们需要在想要用他的时候设置好处理器。
现在我们设置好了Data服务,我们只需要在重新打开应用的时候加载保存的数据即可。所以,现在我们对Location,MyDetails以及Camp Details页进行变更因为他们也需要载入数据。
我们先从Location页开始。
> 修改 src/pages/location/location.ts 的 ionViewDidLoad 如下:

  1. ionViewDidLoad(): void {
  2. this.platform.ready().then(() => {
  3. this.dataService.getLocation().then((location) => {
  4. let savedLocation: any = false;
  5. if(location && typeof(location) != "undefined"){
  6. savedLocation = JSON.parse(location);
  7. }
  8. let mapLoaded = this.maps.init(this.mapElement.nativeElement,
  9. this.pleaseConnect.nativeElement).then(() => {
  10. if(savedLocation){
  11. this.latitude = savedLocation.latitude;
  12. this.longitude = savedLocation.longitude;
  13. this.maps.changeMarker(this.latitude, this.longitude);
  14. }
  15. });
  16. });
  17. });
  18. }

我们这里首先是从缓存里面获取用户位置,然后触发地图的加载。如果缓存中有位置信息的话,那么我们的this.latitudethis.longitude就需要用到这些数据,我们将在地图加载完成的时候将它们提供给changeMaker函数。如果没有存储位置的话我们加载地图就够了。
接下来我们处理Camp Details页。
> 修改 src/pages/camp-details/camp-details.ts页为如下:

  1. import { Component } from '@angular/core';
  2. import { NavController, Platform } from 'ionic-angular';
  3. import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  4. import { Data } from '../../providers/data';
  5. @Component({
  6. selector: 'page-camp-details',
  7. templateUrl: 'camp-details.html'
  8. })
  9. export class CampDetailsPage {
  10. campDetailsForm: FormGroup;
  11. constructor(public navCtrl: NavController, public platform: Platform, public formBuilder: FormBuilder, public dataService: Data) {
  12. this.campDetailsForm = formBuilder.group({
  13. gateAccessCode: [''],
  14. ammenitiesCode: [''],
  15. wifiPassword: [''],
  16. phoneNumber: [''],
  17. departure: [''],
  18. notes: ['']
  19. });
  20. }
  21. ionViewDidLoad(){
  22. this.platform.ready().then(() => {
  23. this.dataService.getCampDetails().then((details) => {
  24. let savedDetails: any = false;
  25. if(details && typeof(details) != "undefined"){
  26. savedDetails = JSON.parse(details);
  27. }
  28. let formControls: any = this.campDetailsForm.controls;
  29. if(savedDetails){
  30. formControls.gateAccessCode.setValue(savedDetails.gateAccessCode);
  31. formControls.ammenitiesCode.setValue(savedDetails.ammenitiesCode);
  32. formControls.wifiPassword.setValue(savedDetails.wifiPassword);
  33. formControls.phoneNumber.setValue(savedDetails.phoneNumber);
  34. formControls.departure.setValue(savedDetails.departure);
  35. formControls.notes.setValue(savedDetails.notes);
  36. }
  37. });
  38. });
  39. }
  40. saveForm(): void {
  41. let data = this.campDetailsForm.value;
  42. this.dataService.setCampDetails(data);
  43. }
  44. }

可以看到我们加上了从数据服务获取数据的调用然后在其中做了一些处理。记得之前我教你在使用Form Builder创建自己的组的时候怎么样去提供默认值的吧:

  1. gateAccessCode: ['value here']

你也许很期待通过使用保存的值作为初始值来创建你自己的表单。不幸的是,他可能不能这么做。取回数据的过程是异步的,虽然数据取回非常的快但是还是有一个等待时间。而Form Builder组需要立刻创建,否则会报错。所以,我们是立刻创建Form Builder组,然后通过this.campDetailsForm.controls来获取他的音乐。然后,我们就可以通过每个空间的setValue方法来设置存储的值。
接着只需要对My Details页做同样的事情就可以了。
> 修改 src/pages/my-details/my-details.ts 为如下:

  1. import { Component } from '@angular/core';
  2. import { NavController, Platform } from 'ionic-angular';
  3. import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  4. import { Data } from '../../providers/data';
  5. @Component({
  6. selector: 'page-my-details',
  7. templateUrl: 'my-details.html'
  8. })
  9. export class MyDetailsPage {
  10. myDetailsForm: FormGroup;
  11. constructor(public nav: NavController, public platform: Platform, public formBuilder: FormBuilder, public dataService: Data) {
  12. this.myDetailsForm = formBuilder.group({
  13. carRegistration: [''],
  14. trailerRegistration: [''],
  15. trailerDimensions: [''],
  16. phoneNumber: [''],
  17. notes: ['']
  18. });
  19. }
  20. ionViewDidLoad() {
  21. this.platform.ready().then(() => {
  22. this.dataService.getMyDetails().then((details) => {
  23. let savedDetails: any = false;
  24. if(details && typeof(details) != "undefined"){
  25. savedDetails = JSON.parse(details);
  26. }
  27. let formControls: any = this.myDetailsForm.controls;
  28. if(savedDetails){
  29. formControls.carRegistration.setValue(savedDetails.carRegistration);
  30. formControls.trailerRegistration.setValue(savedDetails.trailerRegistration);
  31. formControls.trailerDimensions.setValue(savedDetails.trailerDimensions);
  32. formControls.phoneNumber.setValue(savedDetails.phoneNumber);
  33. formControls.notes.setValue(savedDetails.notes);
  34. }
  35. });
  36. });
  37. }
  38. saveForm(): void {
  39. let data = this.myDetailsForm.value;
  40. this.dataService.setMyDetails(data);
  41. }
  42. }

在上面的 Camp Details和 My Details类中,我们都接触了数据服务调用的注释,我们也需要接触Location里面的数据服务调用的注释。
> 修改 src/pages/location/location.ts 的 setLocation() 函数:

  1. setLocation(): void {
  2. Geolocation.getCurrentPosition().then((position) => {
  3. this.latitude = position.coords.latitude;
  4. this.longitude = position.coords.longitude;
  5. this.maps.changeMarker(position.coords.latitude, position.coords.longitude);
  6. let data = {
  7. latitude: this.latitude,
  8. longitude: this.longitude
  9. };
  10. this.dataService.setLocation(data);
  11. let alert = this.alertCtrl.create({
  12. title: 'Location set!',
  13. subTitle: 'You can now find your way back to your camp site from anywhere by clicking the button in the top right corner.',
  14. buttons: [{text: 'Ok'}]
  15. });
  16. alert.present();
  17. });
  18. }

这样就可以了!所有输入到应用里面的数据现在在应用重新加载的时候就可以保持原样了。
我们已经快要来到应用制作的结尾了,显然,我们还要添加一些样式(尽管现在看起来中规中矩),但是下一节课我们要做的是添加一个额外的小功能到应用里。