Получить ссылку на директиву, используемую в компоненте

У меня есть компонент, шаблон которого выглядит примерно так:

<div [my-custom-directive]>Some content here</div>

Мне нужен доступ к используемому здесь экземпляру класса MyCustomDirective. Когда я хочу получить доступ к дочернему компоненту, я использую ViewChild запрос.

Есть ли эквивалентная функция для доступа к дочерней директиве?


person Ben Dilts    schedule 31.03.2016    source источник


Ответы (3)


Вы можете использовать свойство exportAs аннотации @Directive. Он экспортирует директиву, которая будет использоваться в родительском представлении. Из родительского представления вы можете привязать его к переменной представления и получить к нему доступ из родительского класса с помощью @ViewChild().

Пример с плункером:

@Directive({
  selector:'[my-custom-directive]',
  exportAs:'customdirective'   //the name of the variable to access the directive
})
class MyCustomDirective{
  logSomething(text){
    console.log('from custom directive:', text);
  }
}

@Component({
    selector: 'my-app',
    directives:[MyCustomDirective],
    template: `
    <h1>My First Angular 2 App</h1>

    <div #cdire=customdirective my-custom-directive>Some content here</div>
    `
})
export class AppComponent{
  @ViewChild('cdire') element;

  ngAfterViewInit(){
    this.element.logSomething('text from AppComponent');
  }
}

Обновить

Как упоминалось в комментариях, есть еще одна альтернатива вышеупомянутому подходу.

Вместо использования exportAs можно напрямую использовать @ViewChild(MyCustomDirective) или @ViewChildren(MyCustomDirective)

Вот код, демонстрирующий разницу между тремя подходами:

@Component({
    selector: 'my-app',
    directives:[MyCustomDirective],
    template: `
    <h1>My First Angular 2 App</h1>

    <div my-custom-directive>First</div>
    <div #cdire=customdirective my-custom-directive>Second</div>
    <div my-custom-directive>Third</div>
    `
})
export class AppComponent{
  @ViewChild('cdire') secondMyCustomDirective; // Second
  @ViewChildren(MyCustomDirective) allMyCustomDirectives; //['First','Second','Third']
  @ViewChild(MyCustomDirective) firstMyCustomDirective; // First

}

Обновить

Еще один плункер с дополнительными пояснениями

person Abdulrahman Alsoghayer    schedule 01.04.2016
comment
Ответ отличный. Но это также можно сделать без cdire напрямую, например, @ViewChild(MyCustomDirective) element:MyCustomDirective; Then, в ngAfterViewInit - this.element.logSomething('text from...'). Итак, почему ваш подход, когда вы можете сделать это напрямую, просто передав тип? Просто для пояснения. - person micronyks; 01.04.2016
comment
@micronyks Твой подход хорош. однако предполагается, что есть только один MyCustomDirective. Если есть несколько директив, она будет соответствовать первой. Но при использовании exportAs вы можете указать любой, в частности, второй MyCustomDirective. - person Abdulrahman Alsoghayer; 01.04.2016
comment
Использование переменной шаблона упрощает выделение одного элемента, если ваш шаблон содержит более одного элемента, который в противном случае соответствовал бы. Это всегда зависит от того, чего вы на самом деле пытаетесь достичь, и от вашей ситуации. Если вы хотите получить все, вы также должны использовать @ViewChildren() - person Günter Zöchbauer; 01.04.2016
comment
Я согласен, но если у вас более одной директивы, вы также можете передать соответствующий тип. - person micronyks; 01.04.2016
comment
На самом деле, я сам еще не пробовал с директивами, только с компонентами, но из того, что я помню из чтения в документации, он должен работать и с директивами. - person Günter Zöchbauer; 01.04.2016
comment
@micronyks Я обновил свой ответ некоторым кодом. Я надеюсь, что это прояснит разницу между тремя подходами. - person Abdulrahman Alsoghayer; 01.04.2016
comment
@ Абдулрахман. Да, конечно. вы можете сделать это, и это работает. Но я не понял, согласны вы со мной или нет? Ты? - person micronyks; 01.04.2016
comment
посмотрите на это ... те же самые несколько директив и с другой совершенно другой директивой plnkr.co/edit/8RQiStVPx7CCmEIkPsPsPs ? p = предварительный просмотр - person micronyks; 01.04.2016
comment
@micronyks Я согласен с вами, что ваш подход работает. Но то, что я и Гюнтер Цохбауэр пытаемся разъяснить, - это то, что, когда у вас есть несколько экземпляров одной и той же директивы, вы не можете получить доступ к тому, который вам нужен, с помощью ViewChild(), если вы не хотите все или первый экземпляр типа. Я добавил новый плункер - person Abdulrahman Alsoghayer; 01.04.2016
comment
@micronyks в вашем плункере, можете ли вы получить доступ к директиве с содержимым Second без использования exportAs? - person Abdulrahman Alsoghayer; 01.04.2016
comment
Другой вариант - использовать параметр {read: SomeType} в ViewChild, как описано здесь: stackoverflow.com/a/37476195/1736032. Например: @ViewChild('cdire', {read:MyCustomDirective}) secondMyCustomDirective: MyCustomDirective и <div #cdire my-custom-directive>Second</div> (экспорт не требуется). - person dae721; 14.09.2016
comment
Меня также беспокоит то, как можно ссылаться на директиву по шаблону при чтении angular doc angular.io/docs/ts/latest/guide/template-syntax.html #! # ref-vars. Теперь я вижу, что нам нужен exportAs. Очень хороший момент! Спасибо! - person Nguyen Tran; 01.03.2017
comment
@ViewChild(MyCustomDirective, { static: false }) сделает свое дело. По крайней мере, в Angular 8 точно. - person webpreneur; 01.04.2020

Похоже, что после ответа @Abdulrahman директивы больше не могут быть доступны из @ViewChild или @ViewChildren, поскольку они передают только элементы в самом элементе DOM.

Вместо этого вы должны обращаться к директивам с помощью _3 _ / _ 4_.

@Component({
    selector: 'my-app',
    template: `
    <h1>My First Angular 2 App</h1>

    <div my-custom-directive>First</div>
    <div #cdire=customdirective my-custom-directive>Second</div>
    <div my-custom-directive>Third</div>
    `
})
export class AppComponent{
  @ContentChild('cdire') secondMyCustomDirective; // Second
  @ContentChildren(MyCustomDirective) allMyCustomDirectives; //['First','Second','Third']
  @ContentChild(MyCustomDirective) firstMyCustomDirective; // First
}

Также больше нет свойства directives для атрибута @Component.

person Tobias J    schedule 07.04.2017
comment
Экземпляры, созданные с помощью @ContentChild, не определены. Почему ? Например, внутри компонента функция this.firstMyCustomDirective равна undefined - person Saurabh Tiwari; 04.06.2017
comment
ContentChild или ViewChild не работают и возвращают неопределенную переменную - person ninja; 30.08.2017
comment
stackoverflow.com/questions/34326745 / Думаю, есть недоразумения. - person maxisam; 18.11.2017
comment
По состоянию на 2019 год это не кажется правильным, ContentChild и ViewChild, похоже, возвращают undefined в ngAfterViewInit - person EnderWiggin; 10.07.2019

Единственное оставшееся решение с 2019 года

Как упоминалось в комментариях к другим ответам, эти другие (ранее действующие) методы не будут работать с более поздними версиями Angular.


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

@Component({
   // ...
})
export class MyComponent implements OnInit {

  // Would be *undefined*
  // @ContentChild(MyDirective, { static: true })
  // private directive: MyDirective;

  constructor(private directive: MyDirective) { }

  ngOnInit(): void {
    assert.notEqual(this.directive, null); // it passes!
  }
}

Кроме того, вы можете добавить несколько аннотаций, чтобы сообщить механизму внедрения зависимостей, где искать контент для внедрения, используя @ Например, я или @Optional ????

person ccjmne    schedule 24.10.2019
comment
насколько это чище! fwiw it Self() - правильный путь, и значение доступно в конструкторе. (женщина) - person jenson-button-event; 16.01.2020