오픈 소스 분석

오픈소스 분석 - ngbootstrap/pagination.ts

조영재님 2018. 3. 11. 23:54

pagination

디렉터리 구조
pagination
– pagination-config.ts
– pagination.module.ts
– pagination.ts

pagination-config.ts

  • Injectable(주입 가능한) 성질을 가지고 있다
  • NgbPaginationConfig class를 생성하여 pagination.ts 의 input 변수들의 기본값을 설정한다.
  • config : (컴퓨터의) 환경을 설정하다.

pagination.module.ts

  • NgbPagination, NgbPaginationConfig 파일을 import하고, index로 export 한다.
  • 나는 컴포넌트 단위로 기능을 나누곤 했는데, 이는 모듈로 나눈 모습이다.

pagination.ts

ng-bootstrap 모듈에서 불러올 수 있으며, 페이징 기능을 구현해 놓은 코드이다.
component 가 아닌 일반적인 타입스크립트 파일이다.
selector : ‘ngb-pagination’ // html 파일에서 <ngb-pagination …> 이런식으로 불러올 식별자.
changeDetection:ChangeDetectionStrategy.OnPush // ??
host: { ‘role’: ‘navigation’} // ??

template

ul [class]="‘pagination’ + (size ? ’ pagination-’ + size : ’ ')"

  • class를 추가할 때 []로 프로퍼티 바인딩을 사용하여서 typescript의 구문을 사용했다.
  • size가 존재하면 기존의 클래스에 pagination-size 클래스를 추가한다.
  • size -> lg || sm

li _ngIf=“boundaryLinks” class=“page-item” [class.disabled]="!hasPrevious() || disabled"

  • boundaryLinks가 존재할 때, 이 li를 생성하라.
  • boundaryLinks에 컨트롤 됨으로 보아 이 li는 첫 페이지로 가는 버튼임.
  • [class.disabled] 클래스 프로퍼티를 사용했다.
  • 우변의 !hasPrevious() || disabled 의 결과값이 참이면 disabled 클래스를 추가한다.

a aria-label=“First” class=“page-link” href (click)="!!selectPage(1)" [attr.tabindex]="(hasPrevious() ? null : ‘-1’)"

  • click 이벤트 발생 시 !!selectPage(1) 호출. 앞의 !!의 의미는 뭘까?
  • hasPrevious()의 값이 true 이면 tabindex = null, false 이면 tabindex = -1
  • tabindex는 요소에 포커스가 가능한지, 키보드의 탭으로 접근이 가능한지를 설정한다.
  • tabindex = 0 -> tab키나 focus() 메서드로 요소에 포커스를 맞출 수 있다.
  • tabindex= -1 -> tab키로는 접근이 불가능하고, focus()메서드로 접근할 수 있다.

*li ngFor = “let pageNumber of pages” class=“page-item” [class.active]=“pageNumber === page” [class.disabled]=“isEllipsis(pageNumber) || disabled”

  • *ngFor : 타입스크립트의 array형 변수를 가져와 인덱스 하나씩 참조하면서 요소를 반복해서 생성하는 기능, pages 배열을 순회하며 pageNumber변수로 인덱스를 참조한다.

**a ngIf=“isEllipsis(pageNumber)” class=“page-link”>…
ngIf="!isEllipsis(pageNumber)" class=“page-link” href (click)="!!selectPage(pageNumber)" {{pageNumber}}

  • isEllipsis(pageNumber)의 값이 참이면 …링크를 생성하고, 거짓이면 pageNumber가 적힌 링크를 생성한다.
  • isEllipsis(pageNumber): boolean { return pageNumber === -1; }
  • pageNumber가 -1이면 참, 다른 숫자면 거짓.
  • pageNumber가 -1이면 …링크를 생성하고, 아니면 pageNumber가 적힌 링크를 생성한다.

TypeScript : NgbPagination class

normal - variable

  • pageCount = 0; // page버튼의 개수
  • pages: number[] = []; // pageCount만큼 배열크기를 확장해서 ngFor로 출력

Input - property

  • @Input() disabled: boolean; // 제어 가능 여부
  • @Input() boundaryLinks: boolean; // <<, >> 버튼 생성 여부
  • @Input() directionLinks: boolean; // <, > 버튼 생성 여부
  • @Input() ellipses: boolean; // maxSize >pages.size 일 때, … 라벨 생성 여부
  • @Input() rotate: boolean; // 선택된 페이지를 버튼 리스트의 가운데에 고정시킬지 말지…
  • @Input() collectionSize: number; collection의 크기
  • @Input() maxSize: number; // 한번에 보여줄 버튼의 수
  • @Input() page = 0; // 현재 페이지
  • @Input() pageSize: number; // 페이지 당 아이템의 수
  • 전체 페이지 버튼의 수는 collectionSize/pageSize로 연산됨.
  • @Input() size: ‘sm’ | ‘lg’; // 버튼의 크기

Output() - property

  • Output() pageChange = new EventEmitter(true); // 페이지 버튼이 선택되어 바뀔 때마다 발생하는 이벤트를 output.

method

  • construct : 선언된 input variable에 NgbPaginationConfig에서 설정해둔 기본값을 할당한다.

  • hasPrevious(): boolean { return this.page > 1; } // 이전 값이 있는가. page가 1보다 크면 이전 페이지로 갈 수 있으므로 true, 1과 같거나 작다면 갈 수 없으므로 false, -1을 고려하여 > 1 을 사용한듯.

  • hasNext(): boolean { return this.page < this.pageCount; } 위와 같은 함수. 다음 값이 있는가.

  • selectPage(pageNumber: number): void { this._updatePages(pageNumber); } // 페이지 선택시 html에서 호출되는 함수. 클릭한 페이지 버튼의 숫자를 인자로 받는다.

  • ngOnChanges(changes: SimpleChanges): void { this._updatePages(this.page); } // 입력 프로퍼티의 값이 초기화 또는 변경되었을 때, _updatePages 메서드 실행
    ngOnChanges
    부모컴포넌트에서 자식 컴포넌트의 입력 프로퍼티로 바인딩한 값이 초기화 또는 변경되었을 때 실행된다. 따라서 컴포넌트에 입력 프로퍼티가 없는 경우, 호출되지 않는다.
    ngOnChanges는 ngOnInit 이전에 입력 프로퍼티가 존재하는 경우, 최소 1회 호출된다. 이후에는 입력 프로퍼티가 변경될 때마다 반복 호출된다. 이 변경은 입력 프로퍼티의 참조의 변경을 말한다. 다시 말해 기본자료형의 값이 재할당 되었을 때와 객체의 참조가 변경되었을 때만 반응한다.즉 객체의 프로퍼티가 변경되었을 때에는 반응하지 않는다. changes 는 모든 입력 프로퍼티의 이전 값과 현재 값을 포함한다.( PreviousValue, currentValue, firstChange)

  • isEllipsis(pageNumber): boolean { return pageNumber === -1; } // pageNumber 이 -1인지 아닌지를 판단.
    private _updatePages(newPage: number) {
    this.pageCount = Math.ceil(this.collectionSize / this.pageSize); // page버튼의 개수 계산

if(!isNumber(this.pageCount)) { // pageCount가 number형이 아니면 0으로 초기화.
this.pageCount = 0;
}

this.pages.length = 0; // pages 배열 초기화
for (let i = 1; i<=this.pageCount; i++){ // pageCount 만큼 배열 크기 확장
this.pages.push(i);
}

// 인자로 받은 newPage가 현재 페이지와 다를경우 pageChange.emit 이벤트 발생
this._setPageInRange(newPage);

// maxSize(한번에 보여줄 최대 페이지버튼의 수) < pageCount(전체 페이지 버튼의 수) 이면
if (this.maxSize > 0 && this.pageCount > this.maxSize) {
let start = 0;
let end = this.pageCount;

if (this.rotate) {
// rotate=true -> 선택된 페이지 버튼이 항상 가운데 있도록 start, end 값 설정
[start, end] = this._applyRotation();
} else {
// rotate=false -> 선택된 페이지 버튼에 관계없이 maxSize씩 끊어서 보여줌. ex) 1~5, 6~10
[start, end] = this._applyPagination();
}
}
this.pages = this.pages.slice(start, end); 화면에 보여줄 페이지 버튼을 정리. start~ end
this._applyEllipses(start, end); // … 버튼의 추가 여부. -1 -> …버튼, 1,pageCount -> 페이지 숫자버튼