0

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?

1
  • what is the purpose of what u want to do ? u want to get the instance of the component in the same component ? u can use simply this to get the instance , otherwise can u explain more your question so we can answer. Commented Oct 18, 2017 at 11:12

2 Answers 2

1

It sounds like you want to create component which are form controls.

If I'm right, try using ControlValueAccessor instead: https://angular.io/api/forms/ControlValueAccessor

There are plenty of example about how to use them. Here is an example of implementation taken from 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);
  }
}
Sign up to request clarification or add additional context in comments.

Comments

0

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.