任务:
我正在将一个现有的 HTML5 项目(一个基于 Phaser 的科学软件)集成到 Angular 中,目的是更好地构建不断扩展的 UI。
该软件位于其自身的组件中并且正在运行。该软件的所有功能都从一个 Controller 类中公开。我在 Phaser 项目的状态中有一个 Controller 类的实例。
想象一个新组件(例如,顶级菜单)我希望能够说:
<div (click)="this.controller.makeAction()"></div>
,其中“this.controller”是一个实例,保存在顶级菜单组件中。
相关研究:
我在很多地方都读到过,在不相关的组件之间进行通信的推荐方法是通过服务和使用 rxjs BehaviorSubject。好的...问题来了:
问题:
当我创建服务时,我必须设置 BehaviorSubject 的实例。问题是我不知道游戏什么时候准备好,以便访问状态,从而为服务提供 Controller 。所以.. BehaviorSubject 保持为空,我得到一个错误。
我真的很想避免将 Angular 代码放在 Phaser 项目中,因为它们应该尽可能保持分离(目前没有任何耦合)。
问题:
我的方法正确吗? Controller 将提供给任何 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。在服务中设置 Controller 的值
//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。还有其他建议吗?
编辑 2:
根据要求,这是 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');
}
}
最佳答案
正如您所注意到的,您已经使用 setTimeout
解决了问题,这表明导致您的问题的是时间问题。
setTimeout
做了两件事:
首先,它将回调的处理延迟一些指定的毫秒数。这可能会有问题,因为如果用户的浏览器有点慢/更忙,您正在等待的东西准备就绪可能需要超过指定的时间。最好在事件后触发这样的事件,而不是等待时间 - 如果您等待足够长的时间以确保它准备就绪,那么对于大多数用户来说,您等待的时间将比必要的时间长得多。
setTimeout
做的第二件事是将回调的执行推送到下一个 JS 执行帧。这在某些情况下可能是必需的,因为它允许其他排队的工作在继续之前完成。即使您将超时设置为 0 毫秒,这也会起作用 - 如果这解决了问题,那么 setTimeout
可以安全地用于这种情况。
尽管如此,即使您处于这些场景中的第二种情况,结束事件也会更加清晰 - 并且某人或您 future 的自己不太可能出现并在以后取消它!
查看 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);
}
}
关于Angular 6 - 在不相关的组件之间共享一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52059846/