Каков правильный/предпочтительный способ для углового компонента предоставить API (в библиотеке)?

#angular

Вопрос:

Рассмотрим этот упрощенный компонент:

test.component.html

 <button>{{buttonData}}</button>
 

test.component.ts

 import {Component} from '@angular/core';

@Component({
    selector: 'app-test',
    templateUrl: './test.component.html',
    styleUrls: ['./test.component.css']
})
export class TestComponent {
    buttonData: string = 'Test';

    changeButtonContents(data) {
        this.buttonData = data;
    }
}
 

Теперь, рассматривая этот другой компонент:

other.component.html

 <app-test></app-test>
 

Каков наилучший способ предоставления «API» из app-test компонента? Под API я подразумеваю вызываемые методы и настраиваемые переменные. Насколько я знаю, мы можем передавать переменные только как [property]='bindings'

Одним из способов было бы @ViewChild это сделать, но это «кажется» неправильным, и это также дает полный доступ к компоненту, в отличие от предоставления API.

Ответ №1:

Вы не предоставляете «API» (я буду использовать его как термин, чтобы вы лучше понимали, но он неверен), вы можете просто передавать параметры от «Родителя к ребенку» или «От ребенка к родителю» или даже отключенные компоненты (два, которые не являются «Родителем/ребенком», но вам нужно, чтобы параметр из A был получен на B).

В случае @ViewChild , это способ, которым вы можете «устанавливать/обрабатывать» информацию на основе установленного вами «обработчика», чтобы вы могли контролировать определенную информацию, например nativeElement .

Для родителя, как вы сказали [property]="'value'" , и внутренне вы используете @Input()

Таким образом, в данном случае @Input() относится к значению, которое компонент получает от родителя, и , с другой стороны, у вас есть @Output , поэтому дочерний элемент может отправлять значения родителю.

Наконец, в третьем случае вы можете использовать в BehaviorSubject сочетании со службой, чтобы «прослушивать» события, а затем подключать два компонента, которые не имеют таких отношений, как:

 <app-my-general-component>
   <component-a></component-a>
   <component-b></component-b>
</app-my-general-component>
 

Так <component-a> что можете общаться с <component-b>

Наконец, если вам нужно использовать определенную функцию, которая может изменять значения в другом компоненте, то сделайте это с помощью служб, а не самих функций компонента, и подключите оба через эту службу.

Итак, вот и все, в заключение:

  • Если вам нужно, чтобы ребенок получал информацию от родителя, используйте @Input()
  • Если вам нужно, чтобы ребенок отправлял информацию родителю, используйте @Output()
  • Если вам нужны два компонента для подключения, используйте BehaviourSubject

Проверьте официальные документы: https://angular.io/guide/inputs-outputs

Ответ №2:

Почему вы думаете, что подход @ViewChild не очень хорош? Возьмем пример кнопки «Материал». В нем есть метод, называемый focus() , с помощью которого потребители могут сфокусировать кнопку мата.

Теперь, как сделать метод фокусировки вызова потребителя? Используете ViewChild правильно?

 <button mat-raised-button color="primary" #btnRef="matButton">{{'recovery' | translate}}</button>
 

В файле компонента

 @ViewChild('btnRef') buttonRef: MatButton;

ngAfterViewInit() {
   this.buttonRef.focus();
}
 

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

  <mat-select #mySelect placeholder="Favorite food">
    <mat-option *ngFor="let food of foods" [value]="food.value">
      {{ food.viewValue }}
    </mat-option>
  </mat-select>
 

Как мы получаем доступ к API?

  ...
 @ViewChild('mySelect') mySelect;
  ...
  click() {
    this.mySelect.open();
  }
 

Поэтому, на мой взгляд, можно использовать метод API компонентов с помощью ViewChild. Вот как мы захватываем экземпляр компонента и вызываем метод API.