Skip to content

Instantly share code, notes, and snippets.

@ierhalim
Last active September 10, 2021 21:39
Show Gist options
  • Save ierhalim/4e76b7712400d4d8f57b1626bea16ae3 to your computer and use it in GitHub Desktop.
Save ierhalim/4e76b7712400d4d8f57b1626bea16ae3 to your computer and use it in GitHub Desktop.
Karmaşık olarak tanımlanan bileşenin sarmallanması

"Karmaşık olarak tanımlanan bileşeni sarmallayan bileşin dökümantasyonu"

country-selector

Proje içerisinde ülke seçim aracı olarak kullanılır.

Parametreleri

Parametre Tip Açıklama
ariaFilterLabel string Filtre input'u içerisindeki aria-label değeri.
checkbox boolean True olarak tanımlandığında checkbox kullanarak seçim yapmaya yarar.
disabled boolean Seçim aracının etkileşimine açık olup olmadığını belirtir, true ataması yapıldığında bileşeni kullanıcı etkileşimine kapatır.
filter boolean True olarak tanımlandığında bileşene filtreleme desteği ekler.
filterMatchMode string Filterin eşleşme modunu tanımlar alabileceği değerler contains(varsayılan) startsWith, endsWith,equals,notEquals,in,lt,lte,gt and gte.
filterValue string Tanımlandığında filtre bu değerle gözükür
listStyle string Listenin sitil bilgileri
listStyleClass string Listenin sitil sınıfı
multiple boolean True olarak tanımlandığında çoklu seçim modunda çalışır
readonly boolean True olarak tanımlandığında bileşen değeri değiştirilemez
showToggleAll boolean Çoklu seçim özelliğini kullanan örneklerde, true olarak tanımlanırsa bileşene tümünü seçme desteği ekler.
style string Bileşenin sitil bilgileri.
styleClass string Bileşenin sitil sınıfı

Olaylar

Olay Olay Parametreleri Açıklama
onClick value, option Kullanıcı bir liste öğesine tıkladığında tetiklenir
onDblClick value, option Kullanıcı bir liste öğesine iki kere tıkladığında tetiklenir
onChange value Liste öğesinde bir değişiklik olduğunda tetiklenir.

Kullanım Örnekleri

<!--Basit Kullanım !-->
<country-selector></country-selector>
<!-- Parametreler ile kullanımı !-->
<country-selector 
    [multiple]="true"
    [checkbox]="true"
    [showToggleAll]="false"></country-selector>

    <!-- Olay yakalama !-->
    <country-selector 
    (onClick)="handleCountySelectorClicked()"
    (onDblClick)="handleCountrySelectorDblClicked()"
    (onChange)="handleCoutntySelectorChanged()"
    ></country-selector>

<!-- Bu bileşen Template Driven Forms  kullanımı desteklemedir. !-->
<county-selector  name="Country" [(ngModel)]="dataModel.country"></country-selector>

<!-- Bu bileşen Reactive Forms kullanımını desteklemektedir. !-->
<country-selector formControlName="country"></country-selector>
// Karmaşık olarak tanımlanan bileşeni sarmallayan bileşin test implementasyonu
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Listbox } from 'primeng/listbox';
import { By } from '@angular/platform-browser';
import { CountrySelectorComponent } from './country-selector.component';
describe('CountrySelectorComponent', () => {
let component: CountrySelectorComponent;
let fixture: ComponentFixture<CountrySelectorComponent>;
let listBoxComponent: Listbox;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CountrySelectorComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CountrySelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
listBoxComponent = fixture.debugElement.query(
By.css('p-listbox')
).componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Should set default properties of p-listbox', () => {
expect(listBoxComponent.filterPlaceHolder).toBe('Ülke arayın');
expect(listBoxComponent.filterLocale).toBe('tr-TR');
expect(listBoxComponent.emptyFilterMessage).toBe(
'Aradığınız ülke bulunamadı'
);
expect(listBoxComponent.emptyMessage).toBe('Kayıtlı ülke yok');
expect(listBoxComponent.options).toBeDefined();
expect(listBoxComponent.optionLabel).toBe('label');
expect(listBoxComponent.optionValue).toBe('value');
for (let option of listBoxComponent.options) {
expect(option.label).toBeDefined();
expect(option.value).toBeDefined();
}
});
it('Should be able to pass ariaFilterLabel value to p-listbox', () => {
component.ariaFilterLabel = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.ariaFilterLabel).toBe('TestValue');
});
it('Should be able to pass checkbox value to p-listbox ', () => {
component.checkbox = true;
fixture.detectChanges();
expect(listBoxComponent.checkbox).toBe(true);
});
it('Should be able to pass disabled value to p-listbox', () => {
component.disabled = true;
fixture.detectChanges();
expect(listBoxComponent.disabled).toBe(true);
});
it('Should be able to pass filter value to p-listbox', () => {
component.filter = true;
fixture.detectChanges();
expect(listBoxComponent.filter).toBe(true);
});
it('Should be able to pass filterMatchMode value to p-listbox', () => {
component.filterMatchMode = 'eq';
fixture.detectChanges();
expect(listBoxComponent.filterMatchMode).toBe('eq');
});
it('Should be able to pass filterValue value to p-listbox', () => {
component.filterValue = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.filterValue).toEqual('TestValue');
});
it('Should be able to pass listStyle value to p-listbox', () => {
component.listStyle = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.listStyle).toBe('TestValue');
});
it('Should be able to pass listStyleClass value to p-listbox', () => {
component.listStyleClass = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.listStyleClass).toBe('TestValue');
});
it('Should be able to pass metaKeySelection value to p-listbox', () => {
component.metaKeySelection = true;
fixture.detectChanges();
expect(listBoxComponent.metaKeySelection).toBe(true);
});
it('Should be able to pass multiple value to p-listbox', () => {
component.multiple = true;
fixture.detectChanges();
expect(listBoxComponent.multiple).toBe(true);
});
it('Should be able to pass readonly value to p-listbox', () => {
component.readonly = true;
fixture.detectChanges();
expect(listBoxComponent.readonly).toBe(true);
});
it('Should be able to pass optionDisabled value to p-listbox', () => {
component.optionDisabled = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.optionDisabled).toBe('TestValue');
});
it('Should be able to pass showToggleAll value to p-listbox', () => {
component.showToggleAll = true;
fixture.detectChanges();
expect(listBoxComponent.showToggleAll).toBe(true);
});
it('Should be able to pass style value to p-listbox', () => {
component.style = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.style).toBe('TestValue');
});
it('Should be able to pass styleClass value to p-listbox', () => {
component.styleClass = 'TestValue';
fixture.detectChanges();
expect(listBoxComponent.styleClass).toBe('TestValue');
});
it('handleListBoxChange: Should propagete the touched, onChange events.', () => {
const touchedSpy = jasmine.createSpy('TouchedSpy');
const onChangeSpy = spyOn(component.onChange, 'next');
const eventData = { value: 'TestValue' };
component.registerOnTouched(touchedSpy);
component.handleListBoxChange(eventData);
expect(touchedSpy).toHaveBeenCalled();
expect(onChangeSpy).toHaveBeenCalled();
expect(onChangeSpy.calls.mostRecent().args[0]).toEqual(eventData);
});
it('handleListBoxDblClick: Should propagete the touched, onDblClick events.', () => {
const touchedSpy = jasmine.createSpy('TouchedSpy');
const dblClickSpy = spyOn(component.onDblClick, 'next');
const eventData = {
value: 'TestValue',
option: { label: 'TestValue', value: 'TV' },
};
component.registerOnTouched(touchedSpy);
component.handleListBoxDblClick(eventData);
expect(touchedSpy).toHaveBeenCalled();
expect(dblClickSpy).toHaveBeenCalled();
expect(dblClickSpy.calls.mostRecent().args[0]).toEqual(eventData);
});
it('handleListBoxClick: Should propagete the touched, onClick events.', () => {
const touchedSpy = jasmine.createSpy('TouchedSpy');
const clickSpy = spyOn(component.onClick, 'next');
const eventData = {
value: 'TestValue',
option: { label: 'TestValue', value: 'TV' },
};
component.registerOnTouched(touchedSpy);
component.handleListBoxClick(eventData);
expect(touchedSpy).toHaveBeenCalled();
expect(clickSpy).toHaveBeenCalled();
expect(clickSpy.calls.mostRecent().args[0]).toEqual(eventData);
});
it('writeValue: Should be able to set currentValue propert', () => {
component.writeValue('TestValue');
expect(component.currentValue).toBe('TestValue');
component.writeValue(undefined);
expect(component.currentValue).toBe(undefined);
});
it('setDisabledState: Should be able to set disable property', () => {
component.setDisabledState(true);
expect(component.disabled).toBeTrue();
});
it('ngOnDestory: Should complete events',()=>{
const onClickSpy = spyOn(component.onClick, 'complete');
const onChangeSpy = spyOn(component.onChange, 'complete');
const onDblClickSpy = spyOn(component.onDblClick,'complete');
component.ngOnDestroy();
expect(onClickSpy).toHaveBeenCalled();
expect(onChangeSpy).toHaveBeenCalled();
expect(onDblClickSpy).toHaveBeenCalled();
});
});
// Bu örnekte https://www.primetek.com.tr/ tarafından geliştirilen,
// açık kaynak kodlu bir bileşen seti olan PrimeNG kullanılmıştır.
// PrimeNG içerisindeki p-listbox isimli arayüz bileşeni ülke seçim işlemleri için
// özelleştirilmek amacı ile sarmallanmıştır.
// Bileşen bir form bileşenni olduğu için kullanılan frameworkun (Angular)
// form bileşenleri için özel olarak geliştirdiği programamla arayüzü olan ControlValueAccesor kullanılmıştır.
// https://angular.io/api/forms/ControlValueAccessor#description
import {
Component,
EventEmitter,
forwardRef,
Input,
OnDestroy,
Output,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'country-selector',
template: `
<p-listbox
[(ngModel)]="currentValue"
[ngModelOptions]="{ standalone: true }"
[ariaFilterLabel]="ariaFilterLabel"
[checkbox]="checkbox"
[disabled]="disabled"
[filter]="filter"
[filterMatchMode]="filterMatchMode"
[filterValue]="filterValue"
[filterLocale]="filterLocale"
[filterPlaceHolder]="filterPlaceHolder"
[emptyMessage]="emptyMessage"
[emptyFilterMessage]="emptyFilterMessage"
[options]="options"
[optionValue]="optionValue"
[optionLabel]="optionLabel"
[listStyle]="listStyle"
[listStyleClass]="listStyleClass"
(onChange)="handleListBoxChange($event)"
(onDblClick)="handleListBoxDblClick($event)"
(onClick)="handleListBoxClick($event)"
(ngModelChange)="handleNgModelChange()"
></p-listbox>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CountrySelectorComponent),
multi: true,
},
],
})
export class CountrySelectorComponent implements ControlValueAccessor, OnDestroy {
currentValue: any;
readonly filterPlaceHolder = 'Ülke arayın';
readonly filterLocale = 'tr-TR';
readonly emptyFilterMessage = 'Aradığınız ülke bulunamadı';
readonly emptyMessage = 'Kayıtlı ülke yok';
readonly options = [
{ label: 'TÜRKİYE', value: 'TR' },
{ label: 'BİRLEŞİK KRALLIK', value: 'GB' },
{ label: 'BİRLEŞİK DEVLETLER', value: 'US' },
{ label: 'ROMANYA', value: 'RO' },
];
readonly optionLabel = 'label';
readonly optionValue = 'value';
@Input()
ariaFilterLabel: string;
@Input()
checkbox: boolean;
@Input()
disabled: boolean;
@Input()
filter: boolean;
@Input()
filterMatchMode: string;
@Input()
filterValue: string;
@Input()
listStyle: string;
@Input()
listStyleClass: string;
@Input()
metaKeySelection: boolean;
@Input()
multiple: boolean;
@Input()
readonly: boolean;
@Input()
optionDisabled: string;
@Input()
showToggleAll: boolean;
@Input()
style: string;
@Input()
styleClass: string;
handleListBoxChange(event) {
this.propageOnTouched();
this.onChange.next({ value: event.value });
}
handleListBoxDblClick(event) {
this.propageOnTouched();
this.onDblClick.next({ value: event.value, option: event.option });
}
handleListBoxClick(event) {
this.propageOnTouched();
this.onClick.next({ value: event.value, option: event.option });
}
handleNgModelChange() {
this.propageteOnChanged(this.currentValue);
}
@Output()
onChange = new EventEmitter<{ value: string | Array<string> }>();
@Output()
onDblClick = new EventEmitter<{
value: string | Array<string>;
option: { value: string; label: string };
}>();
@Output()
onClick = new EventEmitter<{
value: string | Array<string>;
option: { value: string; label: string };
}>();
propageOnTouched = () => {};
propageteOnChanged = (_) => {};
writeValue(obj: any): void {
this.currentValue = obj;
}
registerOnChange(fn: any): void {
this.propageteOnChanged = fn;
}
registerOnTouched(fn: any): void {
this.propageOnTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
ngOnDestroy(): void{
this.onClick.complete();
this.onDblClick.complete();
this.onChange.complete();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment