Angular 6-在不相关的组件之间共享对象

时间:2018-08-28 14:05:40

标签: angular rxjs communication phaser-framework angular-components

任务:

我正在将现有的HTML5项目(基于Phaser的科学软件)集成到Angular中,以更好地构造不断扩展的UI。

该软件位于其自己的组件中,并且正在运行。该软件的所有功能都通过 controller 类公开。我在Phaser项目状态下有一个控制器类的实例。

想象一个新的组件(例如,顶部菜单),我想说:

<div (click)="this.controller.makeAction()"></div>

,其中“ this.controller”是一个实例,保存在顶部菜单组件中。

相关研究:

我在很多地方都读到,在不相关的组件之间进行通信的推荐方法是通过服务并使用rxjs BehaviorSubject。好的...问题来了:

问题:

创建服务时,必须设置BehaviorSubject的实例。问题是我不知道什么时候游戏才能准备就绪,以便进入状态,从而为服务提供控制器。所以.. BehaviorSubject保持为空,并且出现错误。

我真的想避免将Angular代码放在Phaser项目中,因为应该将它们尽可能地分离(当前没有任何耦合)。

问题:

我的方法正确吗?控制器将被分配给任何UI元素,从而被分配给任何组件。如何解决此问题?

相关代码:

1。服务

// Omitting imports and decorators
export class UserActionControllerService {
  private _userActionController = new BehaviorSubject<UserActionController>(null);
  userActionController = this._userActionController.asObservable();
  constructor() {  }
  setUAC(userActionController: UserActionController){
    this._userActionController.next(userActionController);
  }
}

2。在顶部菜单中使用服务

//Omitting imports and decorator
export class TopMenuComponent implements OnInit {
  userActionController: UserActionController;
  constructor(private uac: UserActionControllerService) {  }
  ngOnInit() {
    this.uac.userActionController.subscribe((value) => {
      this.userActionController = value;
    });
  }

和html ...

<p>{{this.userActionController | async | json}}</p>

3。在服务中设置控制器的值

//Ommitting imports and decorator
export class GteCoreComponent implements OnInit {
  game: Phaser.Game;
  constructor(private userActionControllerService: UserActionControllerService) {}
  ngOnInit() {
    this.game = new GTE(width, height);
this.userActionControllerService.setUAC(this.game.state.states.MainScene.userActionController);
  }
}

最后一行会产生错误,因为尚未创建游戏。我尝试使用setTimeout(),但仍无济于事。

谢谢您的帮助!

编辑:

我设法使其与setTimeout一起使用,这似乎是一种hack。还有其他建议吗?

EDIT2:

根据请求,这是GTE类:

export class GTE extends Phaser.Game {
  game: Phaser.Game;    
  constructor(width?: number, height?: number) {    
    super(width, height, Phaser.CANVAS, 'phaser-div', null, false, true);    
    this.game = this;
    this.game.state.add('Boot', Boot, false);
    this.game.state.add('MainScene', MainScene, false);
    this.game.state.start('Boot');
  }
}

1 个答案:

答案 0 :(得分:0)

正如您所指出的,您已经使用setTimeout解决了问题,这表明时间问题是导致问题的原因。

setTimeout做两件事:

首先,它将回调的处理延迟指定的毫秒数。这可能会出现问题,好像用户的浏览器慢一些/忙一些,可能要花费超过指定时间才能使您等待的事物准备就绪。最好是从事件中触发此类活动,而不是等待一段时间-如果您等待足够长的时间以确保它准备就绪,那么对于大多数用户而言,等待时间将比必要时间长得多。

setTimeout要做的第二件事是将回调的执行推送到下一个JS执行帧。在某些情况下可能需要这样做,因为它允许其他排队的工作在继续之前完成。即使您将超时设置为0ms,这也将起作用-并且如果这可以解决问题,那么在这种情况下,setTimeout是可以安全使用的。

尽管如此,即使您处在第二种情况下,取消某个事件也会更加简洁-某人或您未来的自我出现并在以后取消操作的可能性较小!

看看Phaser.Game documentation,有一个isBooted标志,因此,如果没有暴露事件,则可以在GTE类中创建一个事件:

export class GTE extends Phaser.Game {
  game: Phaser.Game; 
  private readyCallback: () => null;
  constructor(width?: number, height?: number, ready: () => null) {    
    super(width, height, Phaser.CANVAS, 'phaser-div', null, false, true);    
    this.game = this;
    this.game.state.add('Boot', Boot, false);
    this.game.state.add('MainScene', MainScene, false);
    this.game.state.start('Boot');
    this.readyCallback = ready;
    setTimeout(() => this.checkReady(), 100);
  }

  checkReady() {
    if (this.game.isBooted) this.readyCallback();
    else setTimeout(() => this.checkReady(), 100);
  }
}