How to build Menu with Angular7 and Material Design7 → Part2
สืบเนื่องจาก part ที่แล้ว เราได้ลองสร้าง menu ให้กับเว็บไซต์ของเราด้วย angular และ material ไปแล้ว ซึ่งจะเห็นว่าทำได้ง่ายมาก รัน command ไม่กี่คำสั่ง แก้ style sheet อีกนิดหน่อยก็ได้เว็บไซต์พร้อมเมนูแล้ว
ย้อนไปภาคแรก
https://medium.com/printcode/menu-side-nav-with-angular7-and-material-design7-82c156ebb00c
Requirement
ความต้องการเพิ่มเติมในครั้งนี้คือ
- อยากได้เมนูย่อยเพิ่ม
- เมื่อทำการคลิ้กที่ลูกศร ปิด-เปิด เมนูแล้วต้องเห็นเมนูย่อย
- เมื่อคลิ้กที่เมนูย่อยให้เปิดหน้าจอของเมนูนั้นๆออกมา
- เมื่อเข้าไปหน้าจอของเมนูใดๆแล้ว ให้เปลี่ยนสีที่เมนูให้บอกได้ว่ากำลังเข้าเมนูนั้นอยู่ (ในตัวอย่างนี้จะทำการเปลี่ยนสีพื้นหลังของเมนูที่กำลัง active อยู่)
มาเริ่มกันเลย
Step1
สร้าง service สำหรับจัดการข้อมูลของเมนู
ng g s service/menu-service
Step2
สร้าง Interface โครงสร้างเมนู
export interface MenuItem {
group: MenuItem;
menu: Menu[];
}export interface Menu {
code: string;
name: string;
}
Step3
สร้าง function สำหรับเตรียมเมนู
getMenuList() {
const menuList: MenuItem[] = [
{
group: { code: 'menu1', name: 'Menu 1' },
menus: [
{ code: 'subMenu1', name: 'Sub Menu 1' },
{ code: 'subMenu2', name: 'Sub Menu 2' },
{ code: 'subMenu3', name: 'Sub Menu 3' }
]
}
}, {
group: { code: 'menu2', name: 'Menu 2' },
menus: [
{ code: 'subMenu1', name: 'Sub Menu 1' },
{ code: 'subMenu2', name: 'Sub Menu 2' },
{ code: 'subMenu3', name: 'Sub Menu 3' }
]
}
}, {
group: { code: 'menu3', name: 'Menu 3' },
menus: [
{ code: 'subMenu1', name: 'Sub Menu 1' },
{ code: 'subMenu2', name: 'Sub Menu 2' },
{ code: 'subMenu3', name: 'Sub Menu 3' }
]
}
}, {
group: { code: 'menu4', name: 'Menu 4' },
menus: []
}
}, {
group: { code: 'menu5', name: 'Menu 5' },
menus: []
}
}
];
return menuList;
}
จากโค้ดด้านบนคือเราจะสร้างเมนู 4 กลุ่มใหญ่คือ Menu 1–5 และ Menu 1-3 จะมีเมนูย่อยอีก 3 เมนูคือ SubMenu1–3 ส่วน Menu 4–5 ไม่มีเมนูย่อย
Step4 (Optional)
สร้าง function สำหรับดึงชื่อ Menu ของ Sub Menu ขั้นตอนนี้หากให้ต้องการเอาหาชื่อเมนูย่อยเพื่อจะเอาไปแสดงที่หน้าจอก็สามารถทำได้ตามนี้
getSubMenuName(menuCode: string) {
const menuList = this.getMenuList();
for (const group of menuList) {
for (const menu of group.menus) {
return ` > ${menu.name}`;
}
}
return '';
}
Step5
ไปที่ไฟล์ side-nav.component.ts
- ทำการ Inject MenuService เข้ามาใน SideNavComponent
- ประกาศตัวแปร menuList Type MenuItem array
- ทำการดึงค่าของ Menu มาเก็บไว้ที่ menuList
- ประกาศตัวแปร menuGroupSelected Type string
- เพิ่ม function selectMenu สำหรับ event ของการ ปิด-เปิด เมนูย่อย
export class SideNavComponent {
menuGroupSelected: string;
menuList: MenuItem[];
constructor(
...
private menuService: MenuServiceService
) {
this.menuList = this.menuService.getMenuList();
} selectMenu(menuGroup: Menu) {
if (this.menuGroupSelected === menuGroup.code) {
this.menuGroupSelected = null;
return;
}
this.menuGroupSelected = menuGroup.code;
}
}
Step6
ไปที่ไฟล์ side-nav.component.html แก้ไข code ในส่วนของ mat-nav-list เป็นแบบด้านล่างนี้แทน
<mat-nav-list>
<div *ngFor="let menuGroup of menuList">
<div *ngIf="menuGroup.menus.length == 0">
<a mat-list-item class="line" routerLinkActive="active" [routerLink]="['/app/'+menuGroup.group.code]">
<span>{{ menuGroup.group.name }}</span>
</a>
</div>
<div *ngIf="menuGroup.menus.length > 0" [ngClass]="{'group-menu-selected': menuGroupSelected==menuGroup.group.code}">
<a mat-list-item class="line" (click)="selectMenu(menuGroup.group)">
<mat-icon>{{menuGroupSelected==menuGroup.group.code? 'keyboard_arrow_down': 'keyboard_arrow_right'}}</mat-icon>
<span>{{ menuGroup.group.name }}</span>
</a>
<div *ngIf="menuGroupSelected==menuGroup.group.code">
<a *ngFor="let menu of menuGroup.menus" mat-list-item class="line" routerLinkActive="active" [routerLink]="['/app/'+menuGroup.group.code+'/'+menu.code]">
<span>{{ menu.name }}</span>
</a>
</div>
</div>
</div>
</mat-nav-list>
อธิบาย Code แบบสั้นๆ
<!-- วนลูปเมนูใน mat-nav-list -->
<div *ngFor="let menuGroup of menuList"><!-- Code หลังจากบรรทัดนี้คือการ render menu กรณีที่ไม่มี submenu -->
<div *ngIf="menuGroup.menus.length == 0"><!-- Code หลังจากบรรทัดนี้มี logic ใน render group menu และ submenu
<div *ngIf="menuGroup.menus.length > 0">
Step7
ไปที่ไฟล์ side-nav.component.scss แก้ไข style sheet ของเดิมนิดหน่อยตามตัวอย่างโค้ดด้านล่างนี้
.sidenav-container {
...
.mat-nav-list {
padding-top: 0px;
a.active {
background: #3f51b5;
font-weight: bold;
color:#fff;
}
div > div > div > a {
& span {
padding-left: 12px;
}
}
}
.group-menu-selected {
background: #e6e6e6;
}
}
.line {
border-bottom: 0.1px solid #e0e0e0;
}
.mat-list-item {
span {
white-space: nowrap;
}
}
มาดูผลลัพธ์กันหน่อย
Step8
ขั้นตอนต่อไปให้เราไปสร้าง component และ mapping routing ตามจำนวนเมนูที่เราสร้างไว้
ng g c component/menu1
...
ng g c component/menu5
Step9
สร้างไฟล์ routing module
ng g module component --routing
จะได้ไฟล์ที่ชื่อว่า component-routing.module.ts ให้ mapping routing ตามตัวอย่างด้านล่าง
ที่ไฟล์ component.module.ts ให้ประกาศ component ตามนี้
กลับไปที่ไฟล์ app-routing.module.ts ให้เพิ่ม routing ดังนี้
จากตัวอย่างนี้เราใช้วิธีการโหลด component แบบ Lazy load โดยการโหลดของทั้ง module เข้ามาใน child routing เลยซึ่งเมื่อเราคลิ้กเมนูใน module นั้น angular จำทำการโหลด component ที่ถูก mapping path routing ไว้ มาแสดงใน component SideNav โดยผ่าน router outlet
Final Step
มาถึงขั้นตอนสุดท้ายแล้ว
ไปที่ไฟล์ app.component.html ให้ทำการเพิ่ม router outlet
<router-outlet></router-outlet>
ไปที่ไฟล์ side-nav.component.html ให้เพิ่ม router outlet
<!-- Add Content Here -->
<router-outlet></router-outlet>
จบแล้วสำหรับการสร้างเมนูด้วย angular7 และ material design7 ภาค 2 ที่เพิ่มความ Advance ขึ้นมานิดนึง
ตัวอย่างนี้คงเป็นประโยชน์ต่อผู้ที่ผ่านเข้ามาอ่านนะครับ
Source Code: https://gitlab.com/angkarn9/ng7-menu-sidenav
Checkout ไปที่ branch part2 นะครับ