前陣子公司的一個產品算是告一段落,這次使用PrimeNG作為元件的基底,但是在使用的過程中,也是有遇到一些值得記錄的事項,因此特別寫了這篇。
Message
物件的格式是這樣{severity:string, summary:string, details:string}
,但每次都要重複寫那些屬性有點多餘,因此我將這個封裝成service,直接提供success
、error
、info
、warn
四個方法。
然後把message的物件用subject傳送,這樣就能在app.component.html
直接用async的方式接起來。
另外IE11要特別注意一下,details如果沒給值,畫面上會出現null的字,然後就會被殺了orz
這個issue確定會在5.2.4改掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { Injectable } from '@angular/core'; import { Message } from 'primeng/primeng'; import { Subject } from 'rxjs/Subject';
@Injectable() export class MessageService { messages$ = new Subject<Message[]>(); constructor() {}
success(content: string) { this.messages$.next([{ severity: 'success', summary: content, detail: '' }]); } error(content: string) { this.messages$.next([{ severity: 'error', summary: content, detail: '' }]); } info(content: string) { this.messages$.next([{ severity: 'info', summary: content, detail: '' }]); } warn(content: string) { this.messages$.next([{ severity: 'warn', summary: content, detail: '' }]); } }
|
1
| <p-growl [value]="msgService.messages$ | async"></p-growl>
|
這邊提供一個小tip,如果說route有很相似的,但在menu/bread上面不想要被同時選取,這時候可以加上routerLinkActiveOptions: { exact: true }
,這個在angular官網有被提到過。
route的情境像是這樣,沒有加上exact的情況,進入產品頁面的時候,是三個都會被反白,但這基本上不是我們要的效果,因此就要加上,讓他要完整路徑都對才會反白。
1 2 3 4 5
| menuList: MenuItem[] = [ {label: '產品', routerLink: ['/product'], routerLinkActiveOptions: { exact: true }}, {label: '特賣', routerLink: ['/product/special'], routerLinkActiveOptions: { exact: true }}, {label: '團購', routerLink: ['/product/together'], routerLinkActiveOptions: { exact: true }} ];
|
Dialog
包在Component中
如果你也有把dialog整個包進去component的需求,那這個方法就很適合你!
dialog的開啟是透過boolean來控制,當寫成component的時候,參數並不會被改變,這時候dialog就不會再次被開啟,因此我們的好朋友subject
出現啦!
首先child component可以這樣寫
1 2 3
| <p-dialog [(visible)]="displayFlag" [modal]="true"> show something </p-dialog>
|
1 2 3 4 5 6 7 8 9 10
| export class YourComponent implements OnInit{ @Input() data: Subject<boolean>; displayFlag: boolean; ngOnInit() { this.data.subscribe(value => { this.displayFlag = true; }); } }
|
再來parent component的地方,就直接把宣告好的subject
放進去,然後每次要開啟就呼叫subject
1 2
| <button (click)="open()">open dialog</button> <app-your [data]="openDialog$"></app-your>
|
1 2 3 4 5 6 7
| export class ParentComponent { openDialog$ = new Subject<boolean>(); open(){ this.openDialog$.next(true); } }
|
onHide
我們有一個需求是表單填寫的內容放在Dialog中,如果有填寫但最後不儲存,離開前需要提示,像是下面這個範例
1 2 3 4 5 6 7 8 9
| <p-dialog [(visible)]="displayDialog" [modal]="true"> <form [formGroup]="form"> </form> <p-footer> <button pButton type="button" class="ui-button-secondary" label="離開" (click)="close()"></button> <button pButton type="button" class="ui-button-primary" label="儲存" (click)="save()"></button> </p-footer> </p-dialog>
|
1 2 3 4 5 6 7 8 9 10
| @Component() export class MyComponent{ displayDialog: boolean; form: FormGroup; close(){ this.displayDialog = false; } }
|
但是右上角有個X,按的時候也視同關閉,因此我們需要加上一個事件onHide
來做控制,但是當我們像上面範例,把displayDialog
改成false,這時候其實也會觸發onHide
的事件,因此就要稍微注意一下順序的控制,才能避免不斷的觸發,大致的作法像是這樣。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component() export class MyComponent implements OnInit{ displayDialog: boolean; form: FormGroup; valueChange: boolean; ngOnInit(){ this.form.valueChanges .subscribe(p => this.valueChange = true); } close(){ this.displayDialog = false; } hide(){ if(this.valueChange){ } else { this.displayDialog = false; } } }
|
若將file之類的元件放在dialog中,開啟的時候會觸發一個dection change的錯誤,但這個其實不會真的影響功能操作,只是會讓你看起來不開心QQ,當初遇到這個問題時還有去開issue只是官方沒人理我TAT
FileUpload
在使用這個元件的時候,因為畫面的關係,所以採用mode="basic"
,但是這會引發一些狀況,像是maxFileSize
上傳的檔案太大應該要提示訊息,但這種模式下不會…TAT
另外,每次上傳完都要將資料清除,不然再次按上傳的時候,會沒辦法選取直接傳前一次檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Component() export class MyComponent implements OnInit{ @ViewChild(FileUpload) fileUpload: FileUpload; ngOnInit(){ } uploadFile(event) { this.fileService.uploadFile(this.id, this.type, event.files) .subscribe(fileIds => { this.fileUpload.clear(); }); } }
|
DataTable
這邊我們用的是DataTable
而不是新的TurboTable
,就只是因為當時只有一個可以用沒得選XD,這個元件其實蠻多小地方要注意的,很容易萬劫不復阿QQ
官方已經不再維護DataTable這個元件了…請大家轉用TurboTable
Sort
在官方範例中,sort很簡單,就是在column中,加上sortable
這個屬性,但如果你有搭配headerColumnGroup
,那就要注意一下放的位置,除了在下面的column要放field
以外,也要在header中的column加上field
,這樣元件才會知道要排序data中的哪個欄位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <p-dataTable [value]="sales"> <p-headerColumnGroup> <p-row> <p-column header="Brand" rowspan="2"></p-column> <p-column header="Sale Rate" colspan="4"></p-column> </p-row> <p-row> <p-column header="Last Year" [sortable]="true" field="lastYearSale"></p-column> <p-column header="This Year" [sortable]="true"></p-column> <p-column header="Last Year"></p-column> <p-column header="This Year"></p-column> </p-row> </p-headerColumnGroup> <p-column field="brand"></p-column> <p-column field="lastYearSale"></p-column> <p-column field="thisYearSale"></p-column> <p-column field="lastYearProfit"></p-column> <p-column field="thisYearProfit"></p-column> </p-dataTable>
|
另外一個要特別注意的點,就是資料的型別要統一,不然排序也會亂掉
這邊提供一個我做出來的範例,可以發現點year那個欄位,VM這筆資料竟然獨立在那邊,其實原因很簡單,資料中有null
、undefine
、number
三種形態,所以對元件來說會把VM那筆獨立排
1 2 3 4 5 6 7 8 9 10 11 12
| [ {"brand": "VW", year: null, "color": "Orange", "vin": "dsad231ff"}, {"brand": "Audi", "color": "Black", "vin": "gwregre345"}, {"brand": "Renault", "year": 2005, "color": "Gray", "vin": "h354htr"}, {"brand": "BMW", "year": 2003, "color": "Blue", "vin": "j6w54qgh"}, {"brand": "Mercedes", "year": 1995, "color": "Orange", "vin": "hrtwy34"}, {"brand": "Volvo", "color": "Black", "vin": "jejtyj"}, {"brand": "Honda", "year": 2012, "color": "Yellow", "vin": "g43gr"}, {"brand": "Jaguar", "color": "Orange", "vin": "greg34"}, {"brand": "Ford", "year": 2000, "color": "Black", "vin": "h54hw5"}, {"brand": "Fiat", "year": 2013, "color": "Red", "vin": "245t2s"} ]
|
Edit
行內編輯
官方給的範例中,edit的功能只能一個欄位一個欄位的編輯,但如果想要的是整個row一起開放編輯,並且有儲存那些的功能怎麼辦?
這樣的功能我是自己刻,用一個屬性來控制現在是編輯還是檢視
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <p-dataTable [value]="sales" [rowStyleClass]="checkHighlight"> <p-column> <ng-template pTemplate="body" let-row="rowData"> <button *ngIf="!row.isEdit" (click)="row.isEdit=true">Delete</button> <button *ngIf="row.isEdit" (click)="save(row)">Save</button> </ng-template> </p-column> <p-column field="brand" header="brand"> <ng-template pTemplate="body" let-col let-row="rowData"> <span *ngIf="!row.isEdit">{{row[col.field]}}</span> <input type="text" *ngIf="row.isEdit" pInputText [(ngModel)]="row[col.field]"> </ng-template> </p-column> </p-dataTable>
|
編輯highlight
那如果說有需要在編輯的模式中,讓row是呈現被選取的樣式,可以使用rowStyleClass
,可以透過這個方法來指定class
1 2 3 4 5 6
| @Component() export class MyComponent { checkHighlight(row){ return row.isEdit ? 'highlight' : ''; } }
|
目前頁的rowIndex
因為我們會有個需求是要新增資料,但如果用splice到第一筆,但是使用者切換到第二頁,會看不到可以編輯的那筆row,翻了文件才發現有這個屬性first
,這個的值是這頁的第一筆index
依照剛剛的案例,這時候first
會等於10,這時候就可以有兩種作法,第一種是把新的資料直接insert到10的位置,或者是把first
改成0,在這邊我是使用第一個方法
一開始要指定first為0,不然會沒資料
Lazy
在一個feature中,我不小心加上了這個設定,然後資料明明已經寫入超過一頁的量,但畫面永遠只顯示一頁(就爆炸了TAT)
去看了官方文件才發現原來是誤會他的功能,這是用來解決大量資料時透過api來取得分頁資料的功能
以這個範例來看,要使用lazy的功能就必須要套用三個屬性/方法lazy
、totalRecords
、onLazyLoad
,透過這樣的搭配來取得每頁的資料
1 2 3 4 5 6 7 8
| <p-dataTable [value]="cars" [lazy]="true" [rows]="10" [paginator]="true" [rowsPerPageOptions]="[5,10,20]" [totalRecords]="totalRecords" (onLazyLoad)="loadCarsLazy($event)"> <p-header>List of Cars</p-header> <p-column field="vin" header="Vin"></p-column> <p-column field="year" header="Year"></p-column> <p-column field="brand" header="Brand"></p-column> <p-column field="color" header="Color"></p-column> </p-dataTable>
|
2018/05/23 做後續補充…拖有點久QQ
Selection
同事回報的問題,假設目前selectedList
有三筆資料,現在重新取得model
,selectedList
是不會被清除的,而且還會比對不到任何資料所以就不會被選取,因為這時候的物件和前一次不同,
這個行為我不是很確定算不算問題
1 2
| <p-dataTable [value]="model" [(selection)]="selectedList"> </p-dataTable>
|
想要做到固定表頭或是固定前面欄位,可以使用這個功能,但是在設定的時候,發現很容易整個崩壞跑版,尤其是有用到group的功能情況下
目前知道必須要寫死欄位寬高才有辦法比較正常顯示,如果是希望能夠根據資料自動長大的話就…
Dropdown
1
| <p-dropdown [options]="reasonList" [autoWidth]="false" placeholder="請選擇" appendTo="body" formControlName="versionReason"></p-dropdown>
|
如果資料是透過api取得,這時候因為option還沒有任何東西,所以寬度會變得很小要等到點了下拉選單才會變寬,所以要加上autoWidth=false
就可以避免寬度自動縮放。
另外,如果是把dropdown放在dialog裡面,而且下拉選單會超過dialog大小,這時候會發現下拉選單被吃掉了!這時候要加上appendTo="body"
這個設定,讓下拉選單的z-index提高
結論
其實primeng已經可以符合大部分的使用情境,只是有時候官方文件也許寫的沒有很清楚,那跑去看source code(open source 好處XD)或論壇會比較快
primeng團隊也持續的在開發新元件,也不太需要怕沒有人維護,有什麼好的想法也能提供給他們,一起來讓這個元件更好吧!
讀書會分享時,大家的回響讓我勾起回憶,再補上一些
2018/05/23 再補上一些內容