首页 > 解决方案 > 如何在路由器插座中调用同级组件功能并在 Angular 中等待其完成

问题描述

我目前正在开发一个由一系列长表单组成的 Web 应用程序。我有一个全局组件,其模板如下所示:

<app-header></app-header>
<div class="main-container">
  <router-outlet></router-outlet>
</div>
<app-navigation></app-navigation>

所以我有多个页面在路由器插座和 2 个不变的组件内轮流,特别是导航组件,它由允许在不同屏幕/表单之间来回切换的按钮组成。

我的目标是能够从导航组件调用路由器插座内的页面/表单组件的功能,以触发表单验证和强制 Web 服务调用,然后触发导航。

我做了三件事,每个页面/表单都必须实现的接口,它看起来像这样:

export interface FormValidation {
  executeValidation();
}
let subscription;
export function validationEventSubscriber(action: Subject<any>, handler: () => void, off: boolean = false) {
  if (off && subscription) {
    subscription.unsubscribe();
  } else {
    subscription = action.subscribe(() => handler());
  }
}

用于管理包含以下内容的表单的服务:

export class FormsService {

  //...other properties...

  validationSubject = new Subject();

  constructor() { }

  executeValidation() {
    this.validationSubject.next();
  }
  
  //...other functions...
}

还有一个服务来管理导航,它根据不同的业务规则调用 Router.navigateByUrl。

然后在我的 NavigationComponent 我有一个调用的方法executeValidation

  //function called when clicking the button to go to the next page
  nextPage() {
    this.formsService.executeValidation();
  }

最后在页面组件中我有这个:

  constructor(...) {
    this.executeValidation = this.executeValidation.bind(this);
    validationEventSubscriber(this.formsService.validationSubject, this.executeValidation);
  }

  executeValidation() {
    
    //...Validation...

    this.navigationService.nextPage();
  }

这个工作流按预期工作我可以executeValidation从 NavigationComponent 触发页面的功能,但我正在努力实现的是使验证异步。

这里困扰我的是我正在调用NavigationService页面内部,我需要在每个页面中都这样做,在每个executeValidation方法中复制/粘贴同一行代码。我更愿意从内部执行此操作,NavigationComponent但我必须等到页面组件内部的验证结束,这就是我挣扎的地方。

如果有人对如何解决这个问题有任何想法,我会全力以赴!

标签: angularrxjsobservable

解决方案


我倾向于将纯业务逻辑限制在服务中,并将与“视图”部分相关的所有内容都限制在组件中,包括通过路由器进行的导航。

所以,如果这个理念适合你,这就是我要继续的方式。

创建一个myService公开以下内容的服务

private _validate$ = new Subject<bool>();
public validate$ = this._validate$.asObservable();
public executeValidation() {
  this._validate.next(true);
}

myServicein 注入到app-navigationComponent 和所有加载到router-outlet.

然后,您可以创建另一个validationService,在其中定义一个或多个验证方法,这些验证方法调用实际执行工作的 web 服务。

validationService看起来像这样

private _validationResult$ = new Subject<bool>();
public validationResult$ = this_validationResult$.asObservable();
// each form may have its own validation method
// the input of each validation method can be typed via an interface or let any as here
public validateFormX(input: any) {
   // assume validation is a post web service exposed by a remote server
   this.http.post("validationX_url", input).pipe(
     // when the response comes we notify it using _validationResult$
     tap({
      next: resp => this._validationResult$.next(resp),
      error: err => this._validationResult$.error(err)
     }) 
   )
}

我们将 注入validationService到所有加载到router-outlet. 每个组件都必须订阅myService.validate$validationService.validationResult$喜欢这个

// when myService.validate$ notifies, then we launch the validation logic
this.myService.validate$.subscribe(
  // assume each Component in the router-outlet is able to create the input for validation
  const input = buildValidationInput();
  () => this.validationService.validateFormX(input)
)

this.validationService.validationResult$.subscribe(
  validationResp => {
    // depending on the validation response navigate to the corresponding page
    this.router.navigate(....)
  }
)

如果您遵循这种方法,您的优势在于服务中的大多数逻辑都可以比组件更容易测试。


推荐阅读