【问题标题】:Angular 6 - share an object between unrelated componentsAngular 6 - 在不相关的组件之间共享一个对象
【发布时间】:2019-02-03 04:53:42
【问题描述】:

任务:

我正在将一个现有的 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');
  }
}

【问题讨论】:

  • 我认为您应该从 Game Component 发出一个事件,该事件将在 TopMenuComponent 中被监听。类似于init 的东西。然后,您可以通过使用适当的数据在服务的 BehaviorSubject 上调用 next 来执行必要的操作。
  • Phaser.Game 是否有某种事件来指示它何时准备就绪?还是您需要显式调用某些东西以使其准备就绪?
  • @MarkHughes 感谢您的建议。我尝试将最后一行替换为:this.game.load.onLoadComplete(()=&gt;doSomething()),但问题是一样的。游戏尚未在this.game = new GTE(width, height); 行中创建,因此我无法访问它的任何属性。
  • new GTE(...) 做了什么? Phaser.Game 是否有一些文档来说明您应该如何实例化它?
  • ...其实可以把GTE类的代码放在问题里吗?

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


【解决方案1】:

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

setTimeout 做了两件事:

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

setTimeout 所做的第二件事是将回调的执行推送到下一个 JS 执行帧。在某些情况下可能需要这样做,因为它允许其他排队的工作在继续之前完成。即使您将超时设置为 0 毫秒,这也将起作用 - 如果这解决了问题,那么在这种情况下使用 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);
  }
}

【讨论】:

  • 感谢您的建议。如果我在我的 Phaser 游戏中使用自定义事件来执行此操作,这将意味着使用(a)Angular 事件系统,这意味着将 Phaser 游戏耦合到 Angular,或者(b)使用纯 JS 事件系统,我没有觉得会更漂亮。
  • 我希望通过 Phaser.Game 上的 isBooted 标志创建您自己的事件 - 请参阅我的更新答案@MartinAntonov
  • @MartinAntonov 使用 setTimeout 和 isBooted 标志的方法有帮助吗?
  • 嘿。不幸的是,它没有帮助,所以我把它留在了原来的 setTimeout 中。问题是,虽然游戏已经启动,但控制器并没有通过状态创建。
猜你喜欢
  • 2019-09-05
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 2020-04-02
  • 2019-08-07
  • 2020-10-21
  • 1970-01-01
  • 2018-12-27
相关资源
最近更新 更多