Angular 6 - разделить объект между несвязанными компонентами - PullRequest
0 голосов
/ 28 августа 2018

Задача:

Я интегрирую существующий проект HTML5 (научное программное обеспечение на основе Phaser) в Angular, чтобы лучше структурировать постоянно расширяющийся пользовательский интерфейс.

Программное обеспечение находится в своем собственном компоненте и работает. Вся функциональность программного обеспечения предоставляется из класса контроллер . У меня есть экземпляр класса контроллера в состоянии проекта Phaser.

Представляя новый компонент (скажем, в верхнем меню), я бы хотел сказать:

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

, где «this.controller» - это экземпляр, сохраненный в компоненте верхнего меню.

Связанные исследования:

Во многих местах я читал, что рекомендуемый способ связи между несвязанными компонентами - через службу и использование объекта поведения rxjs. Хорошо ... здесь возникает проблема:

Проблема:

Когда я создаю сервис, я должен установить экземпляр BehaviorSubject. Проблема в том, что я не знаю, когда игра будет готова, чтобы получить доступ к состоянию и, таким образом, предоставить контроллер сервису. Итак, объект поведения остается пустым, и я получаю сообщение об ошибке.

Мне бы очень хотелось, чтобы код Angular не помещался в проект Phaser, поскольку он должен быть как можно более разобщенным (в настоящее время нет никакой связи).

Вопросы:

Мой подход правильный? Контроллер будет передан любому элементу пользовательского интерфейса и, следовательно, любому компоненту. Как решить эту проблему?

Код по теме:

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(), но безрезультатно.

Заранее спасибо за помощь!

EDIT:

Мне удалось заставить его работать с setTimeout, что похоже на взлом. Любые другие предложения?

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 голосов
/ 28 августа 2018

Поскольку вы отметили, что решили проблему с помощью setTimeout, это указывает на то, что причиной проблемы является проблема времени.

setTimeout делает две вещи:

Во-первых, это задерживает обработку обратного вызова на некоторое указанное количество миллисекунд. Это может быть проблематично, так как если пользовательский браузер работает немного медленнее / загруженнее, может потребоваться больше, чем указанное время, чтобы то, что вы ожидаете, стало готовым. Всегда лучше инициировать такую ​​активность на событии, чем ждать времени - если вы будете ждать достаточно долго, чтобы убедиться, что оно готово, вы будете ждать гораздо дольше, чем необходимо для большинства пользователей.

Второе, что делает setTimeout, - это отправляет выполнение обратного вызова в следующий кадр выполнения JS. Это может потребоваться в определенных обстоятельствах, поскольку позволяет продолжить работу в очереди перед продолжением. Это будет работать, даже если вы установите тайм-аут на 0 мс - и если это решит проблему, тогда setTimeout безопасно использовать в этом сценарии.

Тем не менее, даже если вы находитесь во втором из этих сценариев, отключение события будет намного чище - и менее вероятно, что кто-то, или ваше будущее я, придут и не сделают это позже!

Глядя на документацию Phaser.Game , есть флаг 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);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...