• 【前端】Angular2 Ionic2 学习记录


    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/angular2_ionic2.html

    一、建立项目

    ionic start ProductName super

    super是ionic提供的一个拥有完整功能的小项目,可以参考学习,如果想创建空项目就用

    ionic start ProductName blank

    当然参数还有很多,比如--no-git等等,可以通过下面的指令查看

    ionic start --help

     

    二、添加i18n国际化

    首先安装需要的依赖,这个版本号是基于@angular/core@4.1.3的,如果@angular/core升级了,这里也应该对应去升级,在添加依赖的时候会提示它需要支持的版本号

    cnpm install --save @ngx-translate/core@6.0.1
    cnpm install --save @ngx-translate/http-loader@0.0.3"

    然后新建 src/assets/i18n/zh.json 文件

    然后在app.module.ts文件中引入

    import {HttpModule, Http} from '@angular/http';
    import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
    import {TranslateHttpLoader} from '@ngx-translate/http-loader';

    在最上面添加下面的方法

    //translate加载器需要知道从ionic的哪个静态管道加载i18文件
    export function HttpLoaderFactory(http: Http) {
      return new TranslateHttpLoader(http, './assets/i18n/', '.json');
    }

    修改imports

    imports: [
      BrowserModule,
      HttpModule,
      TranslateModule.forRoot({
        loader: {
          provide: TranslateLoader,
          useFactory: HttpLoaderFactory,
          deps: [Http]
        }
      }),
      IonicModule.forRoot(MyApp)
    ],

    到此就引入完成,然后就可以用了TranslateService

     

    打开app.component.ts文件,引入TranslateService,然后把它添加到构造器的引用里面private translateService: TranslateService

    import {TranslateService} from '@ngx-translate/core';

    添加一个方法

    initTranslate(){
      this.translateService.setDefaultLang('zh');
    
      if (this.translateService.getBrowserLang() !== undefined) {
        this.translateService.use(this.translateService.getBrowserLang());
      } else {
        this.translateService.use('zh');
      }
    }

    然后在constructor里调用这个方法

    this.initTranslate();

    就可以用了

    this.translateService.get(['tabs.tab1', 'tabs.tab2', 'tabs.tab3', 'tabs.tab4']).subscribe(values => {
        this.tab1Title = values['tabs.tab1'];
        this.tab2Title = values['tabs.tab2'];
        this.tab3Title = values['tabs.tab3'];
        this.tab4Title = values['tabs.tab4'];
    });

     

    三、统一的API数据及错误处理

    如果我们不进行处理,那么每一个请求的subscribe方法我们都要自己写error方法来提示用户网络错误,后台错误,用户未登录等等等等其他各种各样的错误。很显然我们需要一个统一的方法

    首先我们在api.ts最下面添加两个接口(当然你也可以每个做一个单独的文件)来约定我们的后台应该返回什么样格式的数据,还有我们的错误信息应该有哪些数据。习惯了js开发的同学可能会有点陌生,可以把这两个接口理解成两个对象。

    /**
     * 统一的后台返回的数据
     */
    export interface ApiData {
        stateCode: number; //状态码,0表示成功,大于0则表示各种失败的原因(例如1是未登录,2是后台报错等等)
        message?: string; //错误信息,只有在stateCode不为0时才有值
        data?: any; //需要的数据,stateCode为0时才有值
    }
    
    /**
     * 错误信息
     */
    export interface ErrorData {
        errorObservable: Observable<any>; //数据由此而来
        toastController: ToastController; //因为showError是个方法,不方便注入,所以从父级传入。如果想手动注入可以参考ReflectiveInjector.resolveAndCreate方法
    }

     

    然后我们新建一个文件errorInfo.ts文件,这个类用来获取错误信息,返回一个Observable<any>,这个类是全局单例,需要自动注入,所以别忘了加到AppModule里面

    import {Injectable} from '@angular/core';
    import {Observable} from 'rxjs';
    import {TranslateService} from '@ngx-translate/core';
    import {ApiData} from './api';
    
    /**
     * 根据所有数据获取错误信息
     */
    @Injectable()
    export class ErrorInfo {
        // 处理请求被放弃
        private cancel(status, headers, data): Observable<any> {
            return Observable.create(function (observer) {
                observer.next("");
            });
        }
        
        // 处理网络异常
        private network(status, headers, data): Observable<any> {
            return this.translateService.get('stateTexts.networkAnomaly')
        }
        
        // 请求超时
        private timeout(status, headers, data): Observable<any> {
            return this.translateService.get('stateTexts.timeoutException')
        }
        
        // 处理 HTTP 异常
        private http(dstatus, headers, data): Observable<any> {
            return this.translateService.get('stateTexts.serviceException')
        }
        
        // 处理用户未登陆异常
        private notLogin(status, headers, data): Observable<any> {
            //TODO 登录
            return Observable.create(function (observer) {
                observer.next(data.message);
            });
        }
        
        // 处理其它异常操作
        private other(status, headers, data): Observable<any> {
            return Observable.create(function (observer) {
                observer.next(data.message);
            });
        }
        
        constructor(private translateService: TranslateService) {
        }
        
        public getErrorInfo(status: number, headers: any, data?: ApiData): Observable<any> {
            if (status < 200 || status >= 300) {
                return this.http(status, headers, data);
            }
            // 业务异常
            else if (data && data.stateCode) {
                // 用户未登录
                if (data.stateCode == 1) {
                    return this.notLogin(status, headers, data);
                }
                // 其它异常
                else {
                    return this.other(status, headers, data);
                }
            }
            else if (data && data.message) {
                // 其它异常
                return this.other(status, headers, data);
            }
            else {
                //数据格式错误
                return this.http(status, headers, data);
            }
        }
    }

    然后需要在语言文件zh.json文件里添加下面的数据

    "stateTexts": {
        "unknownException": "未知异常,请刷新重试。",
        "notLogin": "用户未登录",
        "networkAnomaly": "网络异常",
        "timeoutException": "请求超时",
        "serviceException": "服务器繁忙,请您稍后再试。"
    }

    然后新建一个showError.ts文件,它只用来export一个function,这玩意就是用来统一处理错误的(虽然它只是创建了一个toast然后展示了一下错误信息)

    import {ToastController} from 'ionic-angular';
    
    /*
     * 统一的Api错误展示
     */
    export function showError(errorData: any) {
        errorData.errorObservable.subscribe(msg => {
            let toast = errorData.toastController.create({
                message: msg,
                duration: 3000,
                showCloseButton: true,
                closeButtonText: '×'
            });
            toast.present();
        });
    }

     toast具体的api可以看官方的文档 http://ionicframework.com/docs/api/components/toast/ToastController/

     

    然后我们来处理api里的get方法,其他方法也是一样的,我就不贴了

    首先需要在constructor里注入我们需要的errorInfo和toastController,然后改写get方法如下

    public get(endpoint: string, params?: any, options?: RequestOptions) {
        if (!options) {
            options = new RequestOptions();
        }
        
        if (params) {
            let par = new URLSearchParams();
            for (let key in params) {
                par.set(key, params[key]);
            }
            
            options.search = !options.search && par || options.search;
        }
        
        let observable = this.http.get(this.url + "/" + endpoint, options);
        
        //这里必须先捕获catch的错误,否则后面throw的错会在这里捕获
    //map是直接返回value,mergeMap是返回Observable.of(value),所以这里要用mergeMap return observable.catch((erro: Response) => { let errorData: ErrorData = Object.create(null); errorData.errorObservable = this.errorInfo.getErrorInfo(erro.status, erro.headers); errorData.toastController = this.toastController; return Observable.throw(errorData); }).mergeMap(res => { let data = res.json();if(data.stateCode != 0){ let errorData: ErrorData = Object.create(null); errorData.errorObservable = this.errorInfo.getErrorInfo(res.status, res.headers, data); errorData.toastController = this.toastController; return Observable.throw(errorData); } else { return Observable.create(function (observer) { observer.next(data.data); observer.complete(); }); } }); }

    Observable.throw会触发subscribe里的error方法,参数也会传过去,我们这里传入了一个ErrorData

    在下面的Observable.create是在网络和数据都没有错误的时候触发

    Observable其他的方法可以参考官方api http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html

     

    至此,我们所有的准备工作都做完了,使用的时候,只需要引入showError,然后在subscribe的第二个参数里填入它就好了

    this.userService.getList().subscribe(
        values => {
            this.users = values.list;
        },
        showError,
        () => console.info("请求已完成"))

    我们后台返回的数据是这样的,但是前端不需要去考虑stateCode、data和message,只需要考虑data里的数据就好

    {
        "stateCode": 0,
        "data": {
            "list": [
                {
                    "id": 1,
                    "username": "静茹♂ 鱼"
                },
                {
                    "id": 2,
                    "username": "一花一世界,一叶一追寻"
                }
            ]
        }
    }

     如果我们请求的地址404的话,就会自动弹出toast了

    四、组件间通讯的MessageCenter

    import {Injectable, EventEmitter} from '@angular/core';
    
    /**
     * 消息中心,提供一个全消息注册及广播服务,作为各模块之间通信使用
     * 
     * this.messageCenter.subonce("msg", data => console.info("第一次订阅", data));
     * this.messageCenter.subonce("msg", data => console.info("第二次订阅", data));
     * this.messageCenter.pubonce("msg", {x: 1, y: 2});
     * 带有once的方法是只能订阅一次,后面订阅的会覆盖前面的,会打印【第二次订阅 {x: 1, y: 2}】
     * 
     * this.messageCenter.subscribe("msg", data => console.info("第一次订阅", data));
     * this.messageCenter.subscribe("msg", data => console.info("第二次订阅", data));
     * this.messageCenter.publish("msg", {x: 1, y: 2});
     * 不带once的方法可以多次订阅,所有订阅的方法都会按顺序执行,会答应【第一次订阅 {x: 1, y: 2}】和【第二次订阅 {x: 1, y: 2}】
     * 
     * 当然,他们是成套使用的
     */
    @Injectable()
    export class MessageCenter {
        //用来存储可以重复订阅的事件
        private eventList: any = {};
        //用来存储只能订阅一次的事件,后面相同的订阅会覆盖前面的
        private onceEventList: any = {};
        
        constructor(){}
        
        /**
         * 发送消息
         * @param messageNames 消息名称
         * @param callback 回调函数
         */
        public subscribe(messageName, callback){
            if(!this.eventList[messageName]){
                this.eventList[messageName] = new EventEmitter();
            }
            this.eventList[messageName].subscribe(callback);
        }
        
        /**
         * 发送消息
         * @param messageNames 消息名称,也可以是数组
         * @param data 需要发送的数据
         */
        public publish(messageNames, data){
            if(typeof messageNames === "string"){
                messageNames = [messageNames];
            }
            for(let i = 0; i < messageNames.length; i++){
                this.eventList[messageNames[i]] && this.eventList[messageNames[i]].emit(data);
            }
        }
        
        /**
         * 用来订阅一次的消息
         * @param messageName 消息名称
         * @param callback 回调函数
         */
        public subonce(messageName, callback){
            this.onceEventList[messageName] = callback;
        }
        
        /**
         * 用来发送订阅一次的消息
         * @param messageNames 消息名称,也可以是数组
         * @param data 需要发送的数据
         */
        public pubonce(messageNames, data){
            if(typeof messageNames === "string"){
                messageNames = [messageNames];
            }
            for(let i = 0; i < messageNames.length; i++){
                this.onceEventList[messageNames[i]] && this.onceEventList[messageNames[i]](data);
            }
        }
    }

    然后就可以随意使用了,特别方便

  • 相关阅读:
    发布基于C#的机器视觉库 *版本1.0.1*
    5月28日介绍这几年机器学习经历的讲演材料
    初识 PLINQ
    推荐博文:鸟瞰淘宝开放平台
    发布一款基于C#的网络爬虫程序
    发布基于C#的网络爬虫程序 *版本1.0.1*
    神秘园与班得瑞
    发布一款基于C#的机器视觉库
    《阿凡达》观影归来
    朝花夕识 和 Google Goggles 的差别
  • 原文地址:https://www.cnblogs.com/shamoyuu/p/angular2_ionic2.html
Copyright © 2020-2023  润新知