Search code examples
angularangular2-components

Getting component inside component which is declared in HTML template


I want to create filter component which will be used in different places with different number of inner components.

filter.component.html

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

Select-filter and input filter are components which implement Interface FilterItem

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

I want to get instance of each component( for example call getValue() ) inside filter.component.ts; What is the best way to do it?


Solution

  • The solution is next

    1) Parent component gets child component's data using @ContentChildren or @ViewChildren with a param as QueryList. One important thing that the parameter is abstract class. We can't use interface there. Actually we can't use abstract class too, but we use providers in nested components. In my case it is

    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) Nested component should extend abstract class and declare this abstract class as a provider. In my case

    @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;
      }
    }
    

    string-filter-item.component.html

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

    filter.component.html

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

    Using filter component anywhere you want ( select string component is another component which I use )

    <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>
    

    That's all! Thanks for you attention!