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;
}
}
Container presenter 패턴 고려해 - 프레젠터 컴포넌트에서는 @Output 이벤트에미터 객체열어서 받고 그걸 템플릿에 반영하고, 컨테이너 컴포넌트에는 onClick클릭한 아이템 이런 식으로 함수 넣어주기
인자를 너무 많으면 클린 코드를 위배하므로 인자 하나로 합치기
네이밍 규칙: onClickHandler(element) → 네이밍이 의미 없음.
// 템플릿:
(click)="onClickListItem(element)"
// 컴포넌트:
onClickListItem(data: AppCrmListData) {
this.router.navigate([
`/app-crm/daily/${data._id}/${data.hospitalTitle}_${data.hospitalId}`,
]);
}
부모-자식 컴포넌트 @Input @Output
@Input
자식 컴포넌트 - item-detail.component.ts
import { Component, Input } from '@angular/core'; // Input 심볼을 로드합니다.
export class ItemDetailComponent {
@Input() item: string; // 프로퍼티에 @Input() 데코레이터를 지정합니다.
}
자식 템플릿 - item-detail.component.html
<p>
Today's item: {{item}}
</p>
부모 컴포넌트 템플릿
<app-item-detail [item]="currentItem"></app-item-detail>
부모 컴포넌트
export class AppComponent {
currentItem = 'Television';
}
이런 식으로 부모컴포넌트에서 소스를 가져와 해당 프로퍼티에 해당하는 값을 자식컴포넌트에서 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>