#angular #rxjs #observable
Вопрос:
В настоящее время я разрабатываю веб-приложение, состоящее из последовательности длинных форм. У меня есть глобальный компонент, шаблон которого выглядит следующим образом :
<app-header></app-header>
<div class="main-container">
<router-outlet></router-outlet>
</div>
<app-navigation></app-navigation>
Таким образом, у меня есть несколько страниц по очереди внутри розетки маршрутизатора и 2 компонента, которые не меняются, в частности компонент навигации, который состоит из кнопок, позволяющих перемещаться туда и обратно между различными экранами/формами.
Цель для меня состояла в том, чтобы иметь возможность вызывать функцию компонента страницы/формы внутри розетки маршрутизатора из компонента навигации для запуска проверки формы и обязательного вызова веб-службы, а затем запускать навигацию.
Я сделал это с помощью трех вещей: интерфейса, который должна реализовать каждая страница/формы и который выглядит следующим образом :
export interface FormValidation {
executeValidation();
}
let subscription;
export function validationEventSubscriber(action: Subject<any>, handler: () => void, off: boolean = false) {
if (off amp;amp; 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 в зависимости от различных бизнес-правил.
Затем в моем навигационном компоненте у меня есть метод, который вызывает 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
функцию страниц из компонента навигации, но чего я изо всех сил пытаюсь добиться, так это сделать проверку асинхронной.
Что меня здесь беспокоит, так это то, что я вызываю NavigationService
внутреннюю часть страницы, и мне нужно делать это на каждой странице, которая копирует/вставляет одну и ту же строку кода в каждый executeValidation
метод. Я бы предпочел сделать это изнутри NavigationComponent
, но мне пришлось бы дождаться завершения проверки внутри компонента страницы, и вот с этим я борюсь.
Если у кого-нибудь есть идея о том, как решить эту проблему, я весь внимание !
Ответ №1:
У меня, как правило, чистая бизнес-логика ограничена услугами, а все, что связано с частью «просмотр», разделено на компоненты, включая навигацию через маршрутизатор.
Итак, если эта философия подходит вам, я бы поступил именно так.
Создайте службу myService
, которая предоставляет следующие возможности
private _validate$ = new Subject<bool>();
public validate$ = this._validate$.asObservable();
public executeValidation() {
this._validate.next(true);
}
myService
в вводится в app-navigation
компонент и во все компоненты, которые загружаются в router-outlet
.
Затем вы можете создать другой validationService
, в котором вы определяете один или несколько методов проверки, которые вызывают веб-сервисы, которые фактически выполняют задание.
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(....)
}
)
Если вы будете следовать этому подходу, у вас будет преимущество в том, что большая часть логики в сервисах, которые вы можете протестировать, намного проще, чем компоненты.
Комментарии:
1. Выглядит многообещающе ! Мне нужно сделать еще кое-что, но я окончательно попробую ваше решение и отчитаюсь. Спасибо!