Ionic3首次项目实践记录
标签(空格分隔): Angular Ionic
Ionic3踩坑
1. 路由懒加载(lazy load)
如果设置了懒加载,就必须全部懒加载(包括TabsPage),否则会出现路由跳转后tabs消失的情况。
2. 通过ts来返回tabs的首页:
注意必须通过this.app.getRootNav().setRoot('tabs');
,不能到home
,否则,tabs会消失。
参见stackoverflow | 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. 父子页面通信
主要是通过NavController
和NavParams
来实现。
父向子直接通过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