• Ionic3项目实践记录


    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. 父子页面通信

    主要是通过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

  • 相关阅读:
    JS的数据类型(包含:7种数据类型的介绍、数据类型的转换、数据类型的判断)
    使用终端改变MAC默认截图存放地址
    Homebrew的安装及使用
    CSS多列布局(实例)
    CSS中元素各种居中方法(思维导图)
    用CSS伪类制作一个不断旋转的八卦图?
    DOSBOX的简单使用
    5分钟读懂JavaScript预编译流程
    基于栈虚拟机和基于寄存器虚拟机的比较
    js执行可视化
  • 原文地址:https://www.cnblogs.com/jehorn/p/9193147.html
Copyright © 2020-2023  润新知