Доступ к экземпляру внешнего компонента из директивы - PullRequest
4 голосов
/ 13 февраля 2020

У меня есть простой компонент с одним входом [id], и шаблон, который использует 'my-directive'.

<div>
    <div my-directive>
        hello world
    </div>
</div>

simple-component будет использоваться несколько раз в шаблоне:

<simple-component [id]="1"></simple-component>
<simple-component [id]="2"></simple-component>

Могу ли я получить доступ к экземпляру simple-component из каждого экземпляра my-directive?

Цель состоит в том, чтобы каждый экземпляр my-directive знал их простой компонент 'host / external', например, для доступа к их свойству 'id', например.

Ответы [ 2 ]

1 голос
/ 13 февраля 2020

Да, существует способ доступа к родительскому компоненту с помощью модификатора разрешения @Host() (подробнее см. официальная документация ). Основная идея c состоит в том, чтобы перемещаться по дереву компонентов с помощью Dependency Injection, чтобы найти ссылку на родительский элемент из дочернего элемента. Есть несколько хороших примеров здесь .

По умолчанию Angular ищет предоставленный экземпляр вверх вплоть до NullInjector (самого высокого в иерархии). Если он не находит экземпляр, он генерирует исключение, если мы не используем @Optional, в этом случае он возвращает null. В вашем конкретном примере c мы используем Host(), чтобы сообщить Angular, что нужно прекратить поиск с этим компонентом в качестве последней остановки при поиске. Нам не нужно его использовать, даже если мы опустим Host(), он все равно будет работать.

В my-directive.directive.ts файле:

constructor(@Host() private parent: SimpleComponent) {
    // here we have an instance of SimpleComponent and we can access its properties except for the Input properties, those are still not set in constructor
}

Я создал простой stackblitz пример, демонстрирующий это.

EDIT : Вот пример , где мы находим экземпляр AppComponent, который является родителем SimpleComponent из директивы . Здесь мы не можем использовать Host(), так как поиск остановится с директивой в качестве последней остановки (и AppComponent выше в цепочке). Поэтому мы просто ничего не добавляем и получаем правильную ссылку.

Надеюсь, это поможет.

0 голосов
/ 13 февраля 2020

Вы можете добиться этого с помощью службы:

@Injectable()
export class IdsTrackerService {
  private ids$$: BehaviorSubject<{ [key: string]: true }> = new BehaviorSubject(
    {}
  );
  public ids$: Observable<string[]> = this.ids$$
    .asObservable()
    .pipe(map(ids => Object.keys(ids)));

  public addId(id: string): void {
    if (!this.ids$$.value[id]) {
      this.ids$$.next({
        ...this.ids$$.value,
        [id]: true
      });
    }
  }
}

Тогда в вашей директиве вам просто нужно зарегистрировать идентификатор при создании директивы и отменить ее регистрацию при уничтожении:

@Directive({
  selector: "[appTrackId]"
})
export class TrackIdDirective implements OnInit, OnDestroy {
  @Input() appTrackId: string | undefined;

  constructor(private idsTrackerService: IdsTrackerService) {}

  public ngOnInit(): void {
    if (!this.appTrackId) {
      throw new Error(`When using "appTrackId" please pass the ID as argument`);
    }

    this.idsTrackerService.addId(this.appTrackId);

    // you now also have access to
    // this.idsTrackerService.ids$ :) !
  }

  public ngOnDestroy(): void {
    this.idsTrackerService.removeId(this.appTrackId);
  }
}

Не забудьте предоставить услугу на уровне компонента , а не на уровне модуля или глобально:

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
  // declare the provider on a component basis
  // so you get a different instance of the service per component
  providers: [IdsTrackerService]
})

Вот демонстрация в реальном времени с отображением отладки для отображения зарегистрированные идентификаторы: https://stackblitz.com/edit/angular-jyvezk

...