Step 1: Define Service B (Validation Service)
Service B will be responsible for validating the input from component A. It will emit an event or a state change when validation criteria are met or not met.
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ValidationService {
private errorState = new BehaviorSubject<string | null>(null);
validateInput(value: string, type: 'name' | 'date'): void {
let isValid = true;
if (type === 'name') {
isValid = value.trim().length > 0;
} else if (type === 'date') {
isValid = /^\d{4}-\d{2}-\d{2}$/.test(value); // Simple YYYY-MM-DD validation
}
this.errorState.next(isValid ? null : 'error');
}
getErrorState() {
return this.errorState.asObservable();
}
}
Step 2: Define Service A (Control Service)
Service A triggers Service B and could handle other logic that coordinates between different parts of your application.
import { Injectable } from '@angular/core';
import { ValidationService } from './validation.service';
@Injectable({
providedIn: 'root'
})
export class ControlService {
constructor(private validationService: ValidationService) { }
processValidation(value: string, type: 'name' | 'date'): void {
this.validationService.validateInput(value, type);
}
}
Step 3: Create a Directive for Handling DOM Manipulations
A directive is a more appropriate place to handle DOM manipulations. It can listen to the validation state and apply classes accordingly.
import { Directive, ElementRef, Renderer2, OnInit, OnDestroy } from '@angular/core';
import { ValidationService } from './validation.service';
import { Subscription } from 'rxjs';
@Directive({
selector: '[appValidate]'
})
export class ValidateDirective implements OnInit, OnDestroy {
private subscription: Subscription;
constructor(
private el: ElementRef,
private renderer: Renderer2,
private validationService: ValidationService
) {}
ngOnInit(): void {
this.subscription = this.validationService.getErrorState().subscribe(state => {
if (state === 'error') {
this.renderer.addClass(this.el.nativeElement, 'error');
} else {
this.renderer.removeClass(this.el.nativeElement, 'error');
}
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
Step 4: Usage in Component A
Finally, ensure that component A uses this directive and connects with the services appropriately.
import { Component } from '@angular/core';
import { ControlService } from './control.service';
@Component({
selector: 'app-component-a',
template: `
<input id="myInput" [(ngModel)]="inputValue" appValidate type="text">
<button (click)="validateInput()">Check</button>
`
})
export class ComponentA {
inputValue: string = '';
constructor(private controlService: ControlService) {}
validateInput(): void {
// Assuming it's a 'name' validation for simplicity
this.controlService.processValidation(this.inputValue, 'name');
}
}
Conclusion
This setup achieves the desired functionality with a clearer separation of concerns:
- Services manage state and logic.
- Directives manage DOM manipulation.
- Components manage user interactions and data binding.
This structure helps keep each part of the app focused on its responsibilities, improving maintainability and testability.