Ionic3项目实践记录

时间:2020-12-10 02:57:57

Ionic3首次项目实践记录

标签(空格分隔): Angular Ionic

Ionic3踩坑

1. 路由懒加载(lazy load)

如果设置了懒加载,就必须全部懒加载(包括TabsPage),否则会出现路由跳转后tabs消失的情况。

2. 通过ts来返回tabs的首页:

注意必须通过 this.app.getRootNav().setRoot('tabs');,不能到home,否则,tabs会消失。

参见* | Issues

import { App } from "ionic-angular";

@IonicPage({ name: [page-name] })
@Component({
...
}) export class DemoPage {
constructor(
private app: App
) { } goBack() {
this.app.getRootNav().setRoot('tabs');
}
}

3. 隐藏子路由里面的tabs,可以通过配置app.module.tas里面的 tabsHideOnSubPages: true实现:

@NgModule({
declarations: [
MyApp
],
imports: [
...
IonicModule.forRoot(MyApp, {
tabsHideOnSubPages: true
}),
...
],
...
})

4. 自定义表单验证

不用angular@Directive()装饰器的方法,直接定义一个类,定义静态方法:

import {FormControl} from "@angular/forms";
import {G} from "../services/data-store.service"; export class MyValidators {
private static isEmptyInputValue(value) {
// we don't check for string here so it also works with arrays
return value == null || value.length === 0;
} /**
* 与指定值相等
* @param {string} equalCtrl 指定FormControl健名
* @returns {(ctrl: FormControl) => {equalTo: {valid: boolean}}}
*/
static equalTo(equalCtrl: string) {
return (ctrl: FormControl) => {
if (this.isEmptyInputValue(ctrl.value)) return null; const _equalCtrl = ctrl.root.get(equalCtrl);
const valid = (_equalCtrl && (ctrl.value === _equalCtrl.value)); return valid ? null : {
equalTo: {
valid: false
}
}
}
}
}

调用方法(FormBui;der):

import {Component, EventEmitter, Output} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {G} from "../../../services/data-store.service";
import {AlertController} from "ionic-angular";
import {StaticDataService} from "../../../services/common/static-data.service";
import {MyValidators} from "../../../directives/my-validators.directive"; @Component({
selector: 'page-sign-up',
templateUrl: 'sign-up.html'
}) export class SignUpPage {
public form: FormGroup;
public TYPES = G.ACCOUNT_TYPES; @Output() onShow = new EventEmitter<number>(); constructor(
private formBuilder: FormBuilder,
private alertCtrl: AlertController,
private staticService: StaticDataService
) {
this.form = this.formBuilder.group({
username: ['', Validators.compose([
Validators.required,
MyValidators.mobile()
])],
password: ['', Validators.compose([
Validators.required,
Validators.minLength(6)
])],
passwordConfirm: ['', Validators.compose([
Validators.required,
MyValidators.equalTo('password')
])],
recommend: ['']
});
} }

html:

...
<ion-item>
<ion-label floating>请输入您的密码</ion-label>
<ion-input type="password" formControlName="password" name="password" class="form-control"></ion-input>
</ion-item>
<div class="error-wrap" [hidden]="form.get('password').valid || form.get('password').pristine">
<small [hidden]="!form.get('password').hasError('required')" class="error">
请输入密码
</small>
<small [hidden]="!form.get('password').hasError('minlength')" class="error">
请输入不少于6位的密码
</small>
</div> <ion-item>
<ion-label floating>请再次确认密码</ion-label>
<ion-input type="password" formControlName="passwordConfirm" name="passwordConfirm" class="form-control"></ion-input>
</ion-item>
<div class="error-wrap" [hidden]="form.get('passwordConfirm').valid || form.get('passwordConfirm').pristine">
<small [hidden]="!form.get('passwordConfirm').hasError('required')" class="error">
请确认密码
</small>
<small [hidden]="!form.get('passwordConfirm').hasError('equalTo')" class="error">
两次密码输入不一致
</small>
</div>
...

5. 父子页面通信

主要是通过NavControllerNavParams来实现。

父向子直接通过push([path], [param], [options])的第二个参数实现数据通信,在子页面中,通过NavParams获取到对应的数据。

子向父pop([options])方法却没有参数传递选项。可以通过Promise实现:

父页:

goToChild() {
new Promise((resolve, reject) => {
this.navCtrl.push('coupon', {
// 将resolve方法传递到子页中
resolve: resolve
});
}).then((data: Datas) => {
// 从子页获取到数据 赋值到当前类的属性中
this.CouponId = data.id;
this.CouponType = data.type;
this.CouponValue = data.value;
});
}

子页:

constructor(
private navParam: NavParams,
private navCtrl: NavController
) {
this.callback = this.navParam.data.resolve;
} ... goBack() {
// 为resolve传值
this.callback({ id: this.selectCoupon, type: this.selectCouponType, value: this.selectCouponValue });
this.navCtrl.pop();
}

6. 在Http通信中,参数中的+号被替换为空格的问题

参见

/**
* 解决http请求字符串中+号被替换为空格的问题
*/
export class CustomQueryEncoderHelper implements HttpParameterCodec {
encodeKey(k: string): string {
return encodeURIComponent(k);
} encodeValue(v: string): string {
return encodeURIComponent(v);
} decodeKey(k: string): string {
return decodeURIComponent(k);
} decodeValue(v: string): string {
return decodeURIComponent(v);
}
}
    // 处理undefined
for (let i in this._datas) {
if (this._datas.hasOwnProperty(i)) {
if (this._datas[i] === undefined) {
delete this._datas[i];
}
}
} Object.assign(datas, this._datas); if (!this.isGet) {
const _date = new Date(Date.parse(new Date().toString()) + CONF.ApiData.DiffTime);
datas.TimeSpan = Tools.dateTimeFormat(_date, 'yyyy-MM-dd hh:mm:ss');
} let preparedParams = new HttpParams({
encoder: new CustomQueryEncoderHelper(),
fromObject: datas
}); if (this.isGet) {
this.datas = { params: preparedParams };
} else {
this.datas = preparedParams;
}

7. Events事件订阅的使用

(个人)开发中最经典的用例,就是在app.component.ts中的ion-menu做页面跳转。由于整个项目是懒加载的,如果直接使用@ViewChild(Nav) nav: Nav;,然后通过this.nav.pus([page])会导致没有tabs,页面一刷新就不能返回了。

经过各种试验,最后发现使用Events事件订阅可以轻松解决。

首先在app.component.ts中发布事件:

import ...
@Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = 'tabs'; constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
private events: Events
) { } // 通知从home页面跳转
get navTo(): Datas {
// 关闭抽屉菜单
this.menuCtrl.close('mainMenu');
return {
publish: name => {
this.events.publish('nav:to', name);
}
}
} // 抽屉菜单打开
menuOpened() {
if (CONF.UserData.IsLogin) {
// 已经登录
this.isLogin = true; this.getUserInfo(); // 获取用户信息
this.getUserFund(); // 获取用户资金账户信息 return;
} // 未登录
this.isLogin = false;
} // 前往设置
goToSetting() {
if (!this.checkLogin()) return; this.navTo.publish('setting');
} ... }

然后在home.ts中订阅事件,做跳转操作,这样就相当于在home中跳转,不会出现tabs丢失的情况:

import ...
@IonicPage({ name: 'home' })
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(private events: Events) {
this.events.subscribe('nav:to', name => {
this.navCtrl.push(name);
});
} ...
}

未完待续...    Last updated by: Jehorn, August 14, 2018, 04:35 PM