پیشنیازهای نمایش اطلاعات گرید به همراه صفحه بندی اطلاعات
در مطلب «Angular CLI - قسمت ششم - استفاده از کتابخانههای ثالث» نحوهی نصب و معرفی کتابخانهی ngx-bootstrap را بررسی کردیم. دقیقا همان مراحل، در اینجا نیز باید طی شوند و از این مجموعه تنها به کامپوننت Pagination آن نیاز داریم. همان قسمت ذیل گرید تصویر فوق که شماره صفحات را جهت انتخاب، نمایش دادهاست.
بنابراین ابتدا فرض بر این است که دو بستهی بوت استرپ و ngx-bootstrap را نصب کردهاید:
> npm install bootstrap --save > npm install ngx-bootstrap --save
"apps": [ { "styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.css" ],
import { PaginationModule } from "ngx-bootstrap"; @NgModule({ imports: [ PaginationModule.forRoot() ]
>ng g m SimpleGrid -m app.module --routing
کامپوننتی هم که مثال جاری را نمایش میدهد به صورت ذیل به ماژول SimpleGrid فوق اضافه شدهاست:
>ng g c SimpleGrid/products-list
تهیه معادلهای قراردادهای سمت سرور در سمت Angular
در قسمت قبل، تعدادی قرارداد مانند پارامترهای دریافتی از سمت کلاینت و ساختار اطلاعات ارسالی به سمت کلاینت را تعریف کردیم. اکنون جهت کار strongly typed با آنها در سمت یک برنامهی تایپ اسکریپتی Angular، کلاسهای معادل آنها را تهیه میکنیم.
ساختار شیء محصول دریافتی از سمت سرور
>ng g cl SimpleGrid/app-product
export class AppProduct { constructor( public productId: number, public productName: string, public price: number, public isAvailable: boolean ) {} }
ساختار معادل پارامترهای صفحه بندی و مرتب سازی ارسالی به سمت سرور
>ng g cl SimpleGrid/PagedQueryModel
export class PagedQueryModel { constructor( public sortBy: string, public isAscending: boolean, public page: number, public pageSize: number ) {} }
ساختار معادل اطلاعات صفحه بندی شدهی دریافتی از سمت سرور
>ng g cl SimpleGrid/PagedQueryResult
export class PagedQueryResult<T> { constructor(public totalItems: number, public items: T[]) {} }
ساختار ستونهای گرید نمایشی
>ng g cl SimpleGrid/GridColumn
export class GridColumn { constructor( public title: string, public propertyName: string, public isSortable: boolean ) {} }
تهیه سرویس ارسال اطلاعات صفحه بندی به سرور و دریافت اطلاعات از آن
پس از تدارک این مقدمات، اکنون کار تعریف سرویسی که این اطلاعات را به سمت سرور ارسال میکند و نتیجه را باز میگرداند، به صورت ذیل خواهد بود:
>ng g s SimpleGrid/products-list -m simple-grid.module
پیش از تکمیل این سرویس، نیاز است متدی را جهت تبدیل یک شیء، به معادل کوئری استرینگ آن تهیه کنیم:
toQueryString(obj: any): string { const parts = []; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (value !== null && value !== undefined) { parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); } } } return parts.join("&"); }
[HttpGet("[action]")] public PagedQueryResult<Product> GetPagedProducts(ProductQueryViewModel queryModel)
http://localhost:5000/api/Product/GetPagedProducts?sortBy=productId&isAscending=true&page=2&pageSize=7
پس از این تعریف، سرویس ProductsListService به صورت ذیل تکمیل خواهد شد:
@Injectable() export class ProductsListService { private baseUrl = "api/Product"; constructor(private http: Http) {} getPagedProductsList( queryModel: PagedQueryModel ): Observable<PagedQueryResult<AppProduct>> { return this.http .get(`${this.baseUrl}/GetPagedProducts?${this.toQueryString(queryModel)}`) .map(res => { const result = res.json(); return new PagedQueryResult<AppProduct>( result.totalItems, result.items ); }); }
سپس در متد map آن، res.json دقیقا همان ساختار PagedQueryResult سمت سرور را به همراه دارد. اینجا است که فرصت خواهیم داشت نمونهی سمت کلاینت آنرا که در ابتدای بحث تهیه کردیم، وهله سازی کرده و بازگشت دهیم (نگاشت فیلدهای دریافتی از سمت سرور به سمت کلاینت).
تکمیل کامپوننت نمایش گرید
قسمت آخر این مطلب، استفادهی از این ساختارها و سرویسها و نمایش اطلاعات دریافتی از آنها است. برای این منظور ابتدا نیاز است سرستونهای این گرید را تهیه کرد:
<table class="table table-striped table-hover table-bordered table-condensed"> <thead> <tr> <th class="text-center" style="width:3%">#</th> <th *ngFor="let column of columns" class="text-center"> <div *ngIf="column.isSortable" (click)="sortBy(column.propertyName)" style="cursor: pointer"> {{ column.title }} <i *ngIf="queryModel.sortBy === column.propertyName" class="glyphicon" [class.glyphicon-sort-by-order]="queryModel.isAscending" [class.glyphicon-sort-by-order-alt]="!queryModel.isAscending"></i> </div> <div *ngIf="!column.isSortable" style="cursor: pointer"> {{ column.title }} </div> </th> </tr> </thead>
export class ProductsListComponent implements OnInit { columns: GridColumn[] = [ new GridColumn("Id", "productId", true), new GridColumn("Name", "productName", true), new GridColumn("Price", "price", true), new GridColumn("Available", "isAvailable", true) ];
همچنین در کدهای قالب این کامپوننت، مدیریت کلیک بر روی یک سر ستون را نیز مشاهده میکنید:
export class ProductsListComponent implements OnInit { itemsPerPage = 7; queryModel = new PagedQueryModel("productId", true, 1, this.itemsPerPage); sortBy(columnName) { if (this.queryModel.sortBy === columnName) { this.queryModel.isAscending = !this.queryModel.isAscending; } else { this.queryModel.sortBy = columnName; this.queryModel.isAscending = true; } this.getPagedProductsList(); } }
کار رندر بدنهی اصلی گرید توسط همین چند سطر در قالب آن مدیریت میشود:
<tbody> <tr *ngFor="let item of queryResult.items; let i = index"> <td class="text-center">{{ itemsPerPage * (currentPage - 1) + i + 1 }}</td> <td class="text-center">{{ item.productId }}</td> <td class="text-center">{{ item.productName }}</td> <td class="text-center">{{ item.price | number:'.0' }}</td> <td class="text-center"> <input id="item-{{ item.productId }}" type="checkbox" [checked]="item.isAvailable" disabled="disabled" /> </td> </tr> </tbody> </table>
در اینجا حلقهای بر روی queryResult.items تشکیل شدهاست. این منبع داده به صورت ذیل در کامپوننت متناظر مقدار دهی میشود:
export class ProductsListComponent implements OnInit { itemsPerPage = 7; currentPage: number; numberOfPages: number; isLoading = false; queryModel = new PagedQueryModel("productId", true, 1, this.itemsPerPage); queryResult = new PagedQueryResult<AppProduct>(0, []); constructor(private productsListService: ProductsListService) {} ngOnInit() { this.getPagedProductsList(); } private getPagedProductsList() { this.isLoading = true; this.productsListService .getPagedProductsList(this.queryModel) .subscribe(result => { this.queryResult = result; this.isLoading = false; }); } }
قسمت آخر کار، افزودن کامپوننت نمایش شماره صفحات است:
<div align="center"> <pagination [maxSize]="8" [boundaryLinks]="true" [totalItems]="queryResult.totalItems" [rotate]="false" previousText="‹" nextText="›" firstText="«" lastText="»" (numPages)="numberOfPages = $event" [(ngModel)]="currentPage" (pageChanged)="onPageChange($event)"></pagination> </div> <pre class="card card-block card-header">Page: {{currentPage}} / {{numberOfPages}}</pre>
export class ProductsListComponent implements OnInit { itemsPerPage = 7; currentPage: number; numberOfPages: number; onPageChange(event: any) { this.queryModel.page = event.page; this.getPagedProductsList(); } }
هر زمانیکه کاربر بر روی شمارهای کلیک میکند، رخداد onPageChange فراخوانی شده و در اینحالت تنها کافی است شماره صفحهی درخواستی queryModel جاری را به روزرسانی کرده و سپس آنرا در اختیار متد getPagedProductsList جهت دریافت اطلاعات این صفحهی درخواستی قرار دهیم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.