import { Component, OnInit, Output, EventEmitter, Input, ViewChild, ElementRef, OnDestroy } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Subscription, Observable, fromEvent } from 'rxjs';
import { map, debounceTime, distinctUntilChanged } from 'rxjs/operators';

import {
  AppCrmHospitalListQuery,
  defaultAppCrmHospitalListQuery
} from '../../services/models';

const DEFAULT_QUERY = defaultAppCrmHospitalListQuery();

@Component({
  selector: 'app-app-crm-hospital-filter',
  templateUrl: './app-crm-hospital-filter.component.html',
  styleUrls: ['./app-crm-hospital-filter.component.scss'],
})
export class AppCrmHospitalFilterComponent implements OnInit {

  inputValue$: Subscription;

  @Input() initialQuery: AppCrmHospitalListQuery;
  @Output() changeFilter = new EventEmitter<AppCrmHospitalListQuery>();

  @Output() value = new EventEmitter<string>();

  @ViewChild('searchTxtInput', { static: true }) searchTxtInput: ElementRef;
  @ViewChild('searchNumInput', { static: true }) searchNumInput: ElementRef;

  TYPE_OPTIONS = [
    {
      text: '전체',
      value: '',
    },
    {
      text: '똑닥 기본 푸시',
      value: 'DEFAULT',
    },
    {
      text: '병원 상세 푸시',
      value: 'HOSPITAL',
    },
  ];

  CRM_TYPE_OPTIONS = [
    {
      text: '전체',
      value: '',
    },
    {
      text: '마케팅',
      value: 'MARKETING',
    },
    {
      text: '사후관리',
      value: 'FOLLOW_MANAGEMENT',
    },
  ];

  form = this.fb.group({
    dateRange: [{
      startDate: DEFAULT_QUERY.startDate,
      endDate: DEFAULT_QUERY.endDate,
    }],
    hospitalTitle: [''],
    hospitalId: [''],
    type: [''],
    crmType: [''],
  });

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.initFormValue();
    this.form.valueChanges.subscribe(() => {
      this.emitListQuery();
    });

    this.inputValue$ = fromEvent(this.searchTxtInput.nativeElement, 'keyup')
      .pipe(
        map((data: any) => data.target.value),
        debounceTime(300),
        distinctUntilChanged()
      ).subscribe(value => {
        this.value.emit(value);
      });

    this.inputValue$ = fromEvent(this.searchNumInput.nativeElement, 'keyup')
      .pipe(
        map((data: any) => data.target.value),
        debounceTime(300),
        distinctUntilChanged()
      ).subscribe(value => {
        this.value.emit(value);
      });
  }

  ngOnDestroy() {
    this.inputValue$.unsubscribe();
  }

  private initFormValue() {
    const query = this.initialQuery;
    const formValue = {
      dateRange: {
        startDate: query.startDate,
        endDate: query.endDate,
      },
      hospitalTitle: '',
      hospitalId: '',
      type: '',
      crmType: '',
    };

    formValue.hospitalTitle = query.hospitalTitle || '';
    formValue.hospitalId = query.hospitalId || '';
    formValue.type = query.type || '';
    formValue.crmType = query.crmType || '';

    this.form.setValue(formValue, { emitEvent: false });
  }

  private emitListQuery() {
    const query = this.toListQuery();
    this.changeFilter.emit(query);
  }

  private toListQuery(): AppCrmHospitalListQuery {
    const value = this.form.value;
    const query: AppCrmHospitalListQuery = {
      startDate: value.dateRange.startDate,
      endDate: value.dateRange.endDate,
    };

    if (value.type) {
      query.type = value.type;
    }

    if (value.crmType) {
      query.crmType = value.crmType;
    }

    if (value.hospitalTitle) {
      query.hospitalTitle = value.hospitalTitle;
    }

    if (value.hospitalId) {
      query.hospitalId = value.hospitalId;
    }

    return query;
  }

}
import { Component, OnInit, Output, EventEmitter, Input, OnDestroy } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

import {
  AppCrmHospitalListQuery,
  defaultAppCrmHospitalListQuery
} from '../../services/models';

const DEFAULT_QUERY = defaultAppCrmHospitalListQuery();

@Component({
  selector: 'app-app-crm-hospital-filter',
  templateUrl: './app-crm-hospital-filter.component.html',
  styleUrls: ['./app-crm-hospital-filter.component.scss'],
})
export class AppCrmHospitalFilterComponent implements OnInit {

  searchFormInput: Subscription;

  @Input() initialQuery: AppCrmHospitalListQuery;
  @Output() changeFilter = new EventEmitter<AppCrmHospitalListQuery>();

  TYPE_OPTIONS = [
    {
      text: '전체',
      value: '',
    },
    {
      text: '똑닥 기본 푸시',
      value: 'DEFAULT',
    },
    {
      text: '병원 상세 푸시',
      value: 'HOSPITAL',
    },
  ];

  CRM_TYPE_OPTIONS = [
    {
      text: '전체',
      value: '',
    },
    {
      text: '마케팅',
      value: 'MARKETING',
    },
    {
      text: '사후관리',
      value: 'FOLLOW_MANAGEMENT',
    },
  ];

  form = this.fb.group({
    dateRange: [{
      startDate: DEFAULT_QUERY.startDate,
      endDate: DEFAULT_QUERY.endDate,
    }],
    hospitalTitle: [''],
    hospitalId: [''],
    type: [''],
    crmType: [''],
  });

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.initFormValue();
    this.searchFormInput = this.form.valueChanges.subscribe(() => {
      debounceTime(300),
        this.emitListQuery();
    });
  }

  ngOnDestroy() {
    this.searchFormInput.unsubscribe();
  }

  private initFormValue() {
    const query = this.initialQuery;
    const formValue = {
      dateRange: {
        startDate: query.startDate,
        endDate: query.endDate,
      },
      hospitalTitle: '',
      hospitalId: '',
      type: '',
      crmType: '',
    };

    formValue.hospitalTitle = query.hospitalTitle || '';
    formValue.hospitalId = query.hospitalId || '';
    formValue.type = query.type || '';
    formValue.crmType = query.crmType || '';

    this.form.setValue(formValue, { emitEvent: false });
  }

  private emitListQuery() {
    const query = this.toListQuery();
    this.changeFilter.emit(query);
  }

  private toListQuery(): AppCrmHospitalListQuery {
    const value = this.form.value;
    const query: AppCrmHospitalListQuery = {
      startDate: value.dateRange.startDate,
      endDate: value.dateRange.endDate,
    };

    if (value.type) {
      query.type = value.type;
    }

    if (value.crmType) {
      query.crmType = value.crmType;
    }

    if (value.hospitalTitle) {
      query.hospitalTitle = value.hospitalTitle;
    }

    if (value.hospitalId) {
      query.hospitalId = value.hospitalId;
    }

    return query;
  }

}

이런 식으로 부모컴포넌트에서 소스를 가져와 해당 프로퍼티에 해당하는 값을 자식컴포넌트에서 input으로 받아 출력하는 것이다. input은 대괄호

@Output

아웃풋은 반대로 자식컴포넌트에서 소스를 가져와 해당 프로퍼티에 해당하는 값을 부모컴포넌트에서 output으로 받아 출력하는 것이다. output은 소괄호

item-output.component.ts (자식)

@Output() newItemEvent = new EventEmitter<string>();

addNewItem(value: string) {
    this.newItemEvent.emit(value);
  }

item-output.component.html (자식 템플릿)

<label>Add an item: <input #newItem></label>
<button (click)="addNewItem(newItem.value)">Add to parent's list</button>

src/app/app.component.ts(부모)

export class AppComponent {
  items = ['item1', 'item2', 'item3', 'item4'];

  addItem(newItem: string) {
    this.items.push(newItem);
  }
}

src/app/app.component.html (부모 템플릿)

<app-item-output (newItemEvent)="addItem($event)"></app-item-output>