需求
保持一个 ShareMoudel 不变,实现前台器具用户和计量机构注销功能
开始思路
1. 增加一个实体
2. 判断是器具用户登录,如果是则调用器具用户的注销,否则调用计量机构的注销(分别显示对应的菜单)
实现
创建一个对象,订阅之前的 $isLogin
如果是true
表示已登录,然后调用对应注销方法,实现代码如下:
// tobar 数组的第一对象的位置,改变位置时需改变 profileMenuIndex
private profileMenuIndex = 0;
tobars: Tobar[] = [
{
title: 'Profile',
class: 'fa fa-fw fa-user',
state: false,
onclickFn: () => {this.router.navigateByUrl('/main/personal');
}
},
{
title: 'Privacy',
class: 'fa fa-fw fa-user-secret',
state: true,
onclickFn: () => {}
},
{
title: 'Settings',
state: true,
class: 'fa fa-fw fa-cog',
onclickFn: () => {}
},
{
title: 'Logout',
state: true,
class: 'fa fa-fw fa-sign-out',
onclickFn: () => {
// 器具用户注销
this.unsubscribeMain = this.departmentService.$isLogin.subscribe((isInstrumentUserLogin) => {if (isInstrumentUserLogin) {this.departmentLogout();
}
});
this.unsubscribeMain.unsubscribe();
// 计量机构注销
this.unsubscribeAdmin = this.systemService.$isLogin.subscribe((isAdminLogin) => {if (isAdminLogin) {this.logout();
}
});
this.unsubscribeAdmin.unsubscribe();}
},
];
ngOnInit(): void {this.departmentService.$isLogin.subscribe((isLogin) => {if (isLogin) {this.showProfileMenu();
}
});
}
// 改变 state,true 显示,false 不显示
showProfileMenu(): void {this.tobars[this.profileMenuIndex].state = true;
}
然而并没有实现
问题:计量机构的注销并不起作用
发现:我订阅的计量机构 $isLogin
不执行,所以说计量机构不能实现注销
看了潘老师之前写的
$isLogin
,发现俩个并不一样private _$isLogin = new
BehaviorSubject<boolean>(false);
public $isLogin = new
Subject<boolean>();
在此感谢张喜硕组长的帮忙!!
BehaviorSubject
与 Subject
区别
Subject
创建一个Rxjs Subject
, 数据的类型是number
let subject1: Subject<number> = new Subject<number>();
然后我们使用 Subject
的next
方法来emit
(发射)1 条数据
subject1.next(1);
接下来对 subject1
创建两个订阅,在 subscription
中直接打印接受到的数据
subject1.subscribe((res: number) => console.info("subjectA", res));
subject1.subscribe((res: number) => console.info("subjectB", res));
接下来我在发射两条数据
subject1.next(2);
subject1.next(3);
结果
subjectA 2
subjectB 2
subjectA 3
subjectB 3
有时候我明明从数据源发射一个数据,但在订阅者拿到的值却是 undefined
或者 null
, 这就是因为订阅者是在数据源发射之后创建的,自然无法接收到数据了。
假如我们想在订阅者创建之后,无论什么时候都能拿到数据,这应该怎么办呢?那就要考虑使用 BehaviourSubject
了。
BehaviourSubject
创建一个 BehaviorSubject
, 默认值设为 0. BehaviorSubject
需要给个默认值
然后发射一条数据 1,创建一个订阅者,再发射一条数据 2,再创建一个订阅者,最后发射一条数据 3。
代码如下:
let subject2: BehaviorSubject<number> = new BehaviorSubject<number>(0);
subject2.next(1);
subject2.subscribe((res: number) => console.info("behavior-subjectA", res));
subject2.next(2);
subject2.subscribe((res: number) => console.info("behavior-subjectB", res));
subject2.next(3);
结果
behavior-subjectA 1
behavior-subjectA 2
behavior-subjectB 2
behavior-subjectA 3
behavior-subjectB 3
由于 BehaviorSubject
是可以存储最后一条数据或者初始默认值的,所以无论订阅者什么时候订阅到数据源 subject2
上, 都能接收到数据。
所以针对订阅者 behavior-subjectA
, 他订阅的时候,数据流里最后一条数据是 1, 他能立即接收到。然后依次能接收到最新的数据 2 和 3。
针对订阅者behavior-subjectB
, 他订阅的时候,数据流里最后一条数据是 2, 他能立即接收到。然后只能能接收到最新的数据 3 了。
上述来源 Subject 四种主题解析,四种 Subject
特点如下:
回到上述问题
所以说在订阅者,订阅他的时候之前的数据他是接受不到的,所以就出现了上述问题,修改之后
虽然是实现了,但是潘老师说这样思路是不对的,如果俩个用户同时登录呢,然后我试了一下果然出现了问题,菜单也不是我想要的了(自己考虑的还是不够啊!!)
新的思路
实现代码如下(及供参考):
- share 组件
app.tobar.service.ts
export class AppTobarService {
constructor(public systemService: SystemService,
public departmentService: DepartmentService,
public router: Router) { }
public $tobars = new BehaviorSubject([
{
title: 'Profile',
class: 'fa fa-fw fa-user',
onclickFn: () => {}
},
{
title: 'Privacy',
class: 'fa fa-fw fa-user-secret',
onclickFn: () => {}
},
{
title: 'Settings',
class: 'fa fa-fw fa-cog',
onclickFn: () => {}
},
{
title: 'Logout',
class: 'fa fa-fw fa-sign-out',
onclickFn: () => {}
},
]);
}
app.tobar.component.ts
ngOnInit(): void {this.appTobarService.$tobars.subscribe((tobars: Tobar[]) => {this.tobars = tobars;});
}
- main 组件
tobar.service.ts
export class TobarService extends AppTobarService {getTobar(): any[] {
return [
{
title: '个人中心',
class: 'fa fa-fw fa-user',
onclickFn: () => {this.router.navigateByUrl('/main/personal');
}
},
{
title: '注销',
class: 'fa fa-fw fa-sign-out',
onclickFn: () => {this.departmentService.loginOut();
}
},
];
}
}
main.component.ts
ngOnInit() {
// 初始化顶部菜
this.appTobarService.$tobars.next(this.tobarService.getTobar());
}
- admin 组件
tobar.service.ts
export class TobarService extends AppTobarService {getTobar(): any[] {
return [
{
title: '系统设置',
class: 'fa fa-fw fa-cog',
onclickFn: () => {this.router.navigateByUrl('/admin/system');
}
},
{
title: '注销',
class: 'fa fa-fw fa-sign-out',
onclickFn: () => {this.systemService.logout();
}
},
];
}
}
admin.component.ts
ngOnInit() {
// 初始化顶部菜单
this.appTobarService.$tobars.next(this.tobarService.getTobar());
}
总结
1. 假设我之前的思路可行,但是在之后修改起来会很麻烦,增加一个菜单时会有很多不定的因素产生(加上时间问题,当时怎么写的已经不记得了,会产生不可控的错误)。
2. 之后的思路,也就是潘老师说的一句话 对扩展开放,对修改关闭
,及时以后增加菜单,Share(模板)中永远保持不变(对修改关闭),想增加菜单只需改Main
或Admin
下的菜单就可以(对扩展开放)。