初探Subject and Subscribe (Ionic4 Angular6)

 阅读约 6 分钟

类似的东西很多,但是不得不说大部分资料中的例子不是难以理解就是形而上学,所以我想分享一点自己的代码来帮助更多初学者。

起源:我打算做一个弹出登录,里面包含一个注册按钮,点击注册则切换到弹出注册,当然在注册时也能切换回登录。在实现的过程中一路踩过以下的坑:

  1. 如果将一个component/page应用到modal中,那么这个component/page必须在不低于弹出modal的层级的module.ts文件中的@NgModule的entryComponents部分声明出来,不然会得到一个错误。
  2. 一个component/page只能在一个module.ts文件中声明
  3. 调用弹出方法的ts文件必须引用使用到的component/page
  4. 如果两个ts文件中都同时引用了对方(a.ts import b, b.ts impot a)那么会得到一个循环依赖的警告,这不影响运行,但是看着闹心。

所以我要再次修改一下,避免这个警告,最后选择了subject的方案。

不要尝试直接复制运行,因为我去掉了和本文无关的部分,比如import { Component } from '@angular/core';这样的语句。

首先在某个单例模式的service中加入如下代码:

import { Subject, Subscription } from 'rxjs';
//公有属性switchModel,这是一个可供订阅的主题
@Injectable({
  providedIn: 'root'
})
export class SiteService {
    public switchModel: Subject<Object> = new Subject<Object>();
}

为了解决问题4,所以弹出的行为将仅在app.component上发生

import { ModalController } from '@ionic/angular';
import { SiteService } from '../services/site/site.service'; //自行替换为声明了subject的service
import { LoginComponent } from '../login/login.component'; //自行替换为你要弹出的内容1
import { SignupComponent } from '../signup/signup.component'; //自行替换为你要弹出的内容2
/*
如果有的话你可以添加更多的内容
注意确认它们被正确的声明在entryComponents部分
对于app.component.ts,其对应的module必然是app.module.ts
*/
export class AppComponent {
    constructor(
        private modalController: ModalController,
        private siteService: SiteService
    ){
        this.siteService.switchModel.subscribe(option => {
            this._switchModel(option);
        });
    }
    private _switchModel(option) {
      let component = null;
      switch(option.componentName) {
          case 'LoginComponent':
              component = LoginComponent;
              break;
          case 'SignupComponent':
              component = SignupComponent;
              break;
          case '': //如果没有指定component那就是直接关闭咯,这是为了在component内部能省掉引用ModalController和关闭modal的代码
              break;
          default:
              return; //这件事和我无关,跳过。防止对其它subscriber负责的component重复处理
      }
      this.modalController.dismiss().then(() => {
          this.modalController.create({
              component: component,
              componentProps: option.params || null
          }).then(modal => modal.present());
      });
    }
}

然后在LoginComponent为注册按钮添加一个事件,SignupComponent做类似的处理

import { SiteService } from '../services/site/site.service';
export class LoginComponent implements OnInit {
    doSwitch() {          
        this.siteService.switchModel.next({
            componentName: 'SignupComponent'
        });
    }
    cancel() {
        this.siteService.switchModel.next({
            componentName: ''
        });
    }
}

逻辑描述:LoginComponent调用Subject的next方法同时传递数据,这样Subject就会将该数据通知到订阅者(AppComponent订阅了这个subject),AppComponent在得到Subject更新的消息时会根据最新的消息做出适当的处理。

理论上来说,我们可以创建一个全局的主题,每个订阅者都可以通过消息的数据类型或其它特征决定自己如何处理,但是个人还是喜欢定制主题从而减少订阅。

刚接触Angular6不久,不管是我这个想法本身有错误还是解决的方式有问题都请拍砖不要客气,只求大侠的砖头上绘制一下示例代码,不胜感激。

阅读 1k发布于 2018-11-07

推荐阅读
你看不见我
用户专栏

文如其人、字如其人、画如其人,程序也是。

2 人关注
10 篇文章
专栏主页
目录