在 HTML 模板中声明的组件中获取组件

Getting component inside component which is declared in HTML template

我想创建过滤器组件,它将在不同的地方使用不同数量的内部组件。

filter.component.html

<select-filter name="somename" ></select-filter>
<input-filter name="somename"></input-filter>
...

Select-filter 和 input filter 是实现 Interface FilterItem

的组件
export interface FilterItem{
  name: string;
  getValue() : any;
}

我想在 filter.component.ts 中获取每个组件的实例(例如调用 getValue() ); 最好的方法是什么?

听起来您想创建作为表单控件的组件。

如果我是对的,请尝试使用 ControlValueAccessorhttps://angular.io/api/forms/ControlValueAccessor

有很多关于如何使用它们的示例。 这是取自 https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html

的实施示例
export function createCounterRangeValidator(maxValue, minValue) {
  return (c: FormControl) => {
    let err = {
      rangeError: {
        given: c.value,
        max: maxValue || 10,
        min: minValue || 0
      }
    };

  return (c.value > +maxValue || c.value < +minValue) ? err: null;
  }
}

@Component({
  selector: 'counter-input',
  template: `
    <button (click)="increase()">+</button> {{counterValue}} <button (click)="decrease()">-</button>
  `,
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterInputComponent), multi: true },
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => CounterInputComponent), multi: true }
  ]
})
export class CounterInputComponent implements ControlValueAccessor, OnChanges {

  propagateChange:any = () => {};
  validateFn:any = () => {};

  @Input('counterValue') _counterValue = 0;
  @Input() counterRangeMax;
  @Input() counterRangeMin;

  get counterValue() {
    return this._counterValue;
  }

  set counterValue(val) {
    this._counterValue = val;
    this.propagateChange(val);
  }

  ngOnChanges(inputs) {
    if (inputs.counterRangeMax || inputs.counterRangeMin) {
      this.validateFn = createCounterRangeValidator(this.counterRangeMax, this.counterRangeMin);
      this.propagateChange(this.counterValue);
    }
  }

  writeValue(value) {
    if (value) {
      this.counterValue = value;
    }
  }

  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  registerOnTouched() {}

  increase() {
    this.counterValue++;
  }

  decrease() {
    this.counterValue--;
  }

  validate(c: FormControl) {
    return this.validateFn(c);
  }
}

接下来就是解决方案

1) 父组件使用@ContentChildren 或@ViewChildren 获取子组件的数据,参数为QueryList。参数是抽象的 class 很重要。我们不能在那里使用接口。其实我们也不能使用abstract class,而是在嵌套组件中使用provider。 在我的例子中是

export class FilterComponent{
   @ContentChildren(FilterItem) filterItems: QueryList<FilterItem>;
   constructor() {}

   getItems(){
      console.log(this.filterItems);
      this.filterItems.forEach( i => {
        console.log( 'My name is ' + i.name + 'My value is ' + i.getValue());
      });
   }
}

2) 嵌套组件应扩展抽象 class 并将此抽象 class 声明为提供者。 就我而言

@Component({
  selector: 'app-string-filter-item',
  templateUrl: './string-filter-item.component.html',
  styleUrls: ['./string-filter-item.component.scss'],
  providers: [{provide: FilterItem, useExisting: forwardRef(() => StringFilterItemComponent)}]
})
export class StringFilterItemComponent extends FilterItem {

  selectValue: string;

  @Input()
  name:string;

  caption: 'SHow me smt';

  getValue(){
     return this.selectValue;
  }
}

字符串过滤器-item.component.html

<p>
  <input type="text" [(ngModel)]="selectValue">
</p>

filter.component.html

<div class="filter-wr">
   <ng-content></ng-content>
</div>

在任何地方使用过滤器组件 ( select 字符串组件是我使用的另一个组件)

<app-filter>
  <app-select-filter-item name="first"></app-select-filter-item>
  <app-string-filter-item name="second"></app-string-filter-item>
  <app-select-filter-item name="third"></app-select-filter-item>
  <app-string-filter-item name="fourth"></app-string-filter-item>
</app-filter>

就是这样!感谢您的关注!