• Maintaining Separation of Concerns Through Angular Directives
  • Author: Netanel Basal
  • The Nuggets translation Project
  • Permalink: Maintain separation of concerns through Angular directives
  • Translator: Front-end picker
  • Proofreader: None (Your comments are welcome, you can spray me)

Suppose we have a date picker component in our application. Each time a user changes a date, an event is sent to the analytics provider. We’ve only used it once so far, so the profiling interface can be placed in components that use it:

header-1.ts

import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroyed';

@UntilDestroy()
class FooComponent {
  timespanControl = new FormControl();

  ngOnInit() {
    this.timespanControl.valueChanges
      .pipe(untilDestroyed(this))
      .subscribe(({ preset }) = > {
        this.analyticsService.track('timespan-filter apply', {
          value: preset, }); }); }}Copy the code

However, now that we have more places to use this analysis interface, we don’t want to write the same code over and over again. One might suggest that this code could be incorporated into the date picker and passed as an input parameter.

data-picker-1.component.ts

class DatePickerComponent {
  @Input() analyticsContext: string;
   
  constructor(private analyticsService: AnalyticsService) {}

  apply() {
    this.analyticsService.track('timespan-filter apply', {
      context: this.analyticsContext,
      value: this.preset, }); . }}Copy the code

Yes, this can be done, but it’s not ideal design. Separation of concerns means that the date picker itself is independent of the analysis interface and does not need to know anything about the analysis interface.

In addition, since the date picker is an internal component, we can modify its source code, but if it is a third-party component? How to solve it?

The better choice here is the Angular directive, which creates a directive that gets a reference to the form via DI and subscribes to internal value changes to trigger the analysis event. datePickerAnalytics.directive.ts

@UntilDestroy()
@Directive({
  selector: '[datePickerAnalytics]',})export class DatePickerAnalyticsDirective implements OnInit {
  @Input('datePickerAnalytics') analyticsContext: string;

  constructor(private dateFormControl: NgControl, private analyticsService: AnalyticsService) {}

  ngOnInit() {
    this.dateFormControl
      .control.valueChanges.pipe(untilDestroyed(this))
      .subscribe(({ preset }) = > {
        this.analyticsService.track(
          'timespan-filter apply',
          {
            value: preset,
            context: this.analyticsContext } ); }); }}Copy the code

You can now use it every time you use the date picker.

<date-picker [formControl]="control" datePickerAnalytics="fooPage"></date-picker>
Copy the code