How to check if another directive is present on the same element
Asked Answered
E

2

8

Suppose I have two directives:

@Directive({
  selector: '[appDirective1]'
})
export class Directive1Directive {

  public alone = true;

  constructor(private element: ElementRef<HTMLInputElement>) { }

  ngOnInit() {
    if (this.alone) {
      this.element.nativeElement.innerHTML = "I am alone";
    } else {
      this.element.nativeElement.innerHTML = "I have a friend";
    }
  }
}

@Directive({
  selector: '[appDirective2]'
})
export class Directive2Directive {

  constructor() { }

}

I want Directive1 to change its behavior if Directive2 is present on the same element. For example, these should have different behavior.

<div appDirective1></div>
<div appDirective1 appDirective2></div>

Is it possible for me to do this without adding a service to facilitate communication*?

StackBlitz Example: https://stackblitz.com/edit/angular-bgqd15


*I would like to avoid creating a new service for this because that feels like overkill.

Elephant answered 1/8, 2019 at 15:44 Comment(0)
D
10

You can inject the neighboring directives via the constructor using a Host() decorator, and also make it Optional()

  constructor(@Optional() @Host() public friend: Directive2Directive) { }

  ngOnInit() {
    if (this.friend) {
      this.element.nativeElement.innerHTML = "I have a friend";
    } else {
      this.element.nativeElement.innerHTML = "I am alone";
    }
  }

Components, directives and providers are injectable and you can limit DI to only search on the current host level.

Decarbonate answered 1/8, 2019 at 15:53 Comment(4)
Is there a reason you'd prefer @Host over @Self (angular.io/api/core/Self)? I'm not super familiar with the difference, but my initial impression is that @Host will cast a wider netElephant
@Elephant @Self() starts at the current host and walks upwards until it finds the provider. @Host() only looks at the current host and stops if it is not found.Decarbonate
Wow, so @Self is actually the wider net! Thanks for the information!Elephant
@Elephant I have a feeling that @Self() is the default, because we also have @SkipSelf() so what is the difference when you don't use @Self()? So that's why I think it's the default, but I checked the docs and can't confirm it there.Decarbonate
E
2

I consider Reactgular's answer to be superior because it can be easily be applied to either directive as required.


Depending on the relationship between the two directives, I have found the following solutions:

Let Directive1 explicitly check for Directive2:

@Directive({
  selector: '[appDirective1]'
})
export class Directive1Directive {

  public alone = true;

  @Input("appDirective2")
  public appDirective2: any;

  constructor(private element: ElementRef<HTMLInputElement>) { }

  ngOnInit() {
    if (this.appDirective2 !== undefined) {
      this.alone = false;
    }
    if (this.alone) {
      this.element.nativeElement.innerHTML = "I am alone";
    } else {
      this.element.nativeElement.innerHTML = "I have a friend";
    }
  }
}

StackBlitz example: https://stackblitz.com/edit/angular-5w63vu

Provide a hook for Directive2 to tell Directive1:

@Directive({
  selector: '[appDirective1]'
})
export class Directive1Directive {

  public alone = true;

  constructor(private element: ElementRef<HTMLInputElement>) { }

  ngOnInit() {
    this.setContent();
  }

  setFriend() {
    this.alone = false;
    this.setContent();
  }

  setContent() {
    if (this.alone) {
      this.element.nativeElement.innerHTML = "I am alone";
    } else {
      this.element.nativeElement.innerHTML = "I have a friend";
    }
  }
}

@Directive({
  selector: '[appDirective2]'
})
export class Directive2Directive {

  constructor(@Self() private otherDirective: Directive1Directive ) { }

  ngOnInit() {
    this.otherDirective.setFriend();
  }
}

StackBlitz example: https://stackblitz.com/edit/angular-bag8wa

Elephant answered 1/8, 2019 at 15:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.