วิธีสร้างและปรับใช้แอปพลิเคชันวัสดุเชิงมุม
เผยแพร่แล้ว: 2022-03-10Angular เป็นหนึ่งในตัวเลือกยอดนิยมในขณะที่สร้างเว็บแอปพลิเคชันใหม่ ยิ่งไปกว่านั้น สเปก “ดีไซน์ Material” ได้กลายเป็นตัวเลือกยอดนิยมสำหรับการสร้างประสบการณ์ที่น้อยที่สุดและน่าดึงดูดใจในปัจจุบัน ดังนั้น โครงการ "Angular" ใหม่ส่วนใหญ่จึงใช้ "Angular Material Design Library" เพื่อใช้ส่วนประกอบที่เป็นไปตามข้อกำหนดการออกแบบวัสดุ ตั้งแต่แอนิเมชั่นที่ลื่นไหลไปจนถึงการตอบสนองการโต้ตอบที่เหมาะสม ทั้งหมดนี้พร้อมให้ใช้งานแล้วโดยเป็นส่วนหนึ่งของไลบรารีดีไซน์ Material อย่างเป็นทางการสำหรับ angular
หลังจากพัฒนาเว็บแอปพลิเคชันแล้ว ขั้นตอนต่อไปคือการปรับใช้ นั่นคือจุดที่ “Netlify” เข้ามาในภาพ ด้วยอินเทอร์เฟซที่ใช้งานง่าย การปรับใช้อัตโนมัติ การแยกการรับส่งข้อมูลสำหรับการทดสอบ A/B และคุณสมบัติอื่นๆ มากมาย Netlify เป็นเครื่องมือที่ยอดเยี่ยมอย่างแน่นอน
บทความนี้จะเป็นบทสรุปของการสร้างเว็บแอปพลิเคชัน Angular 8 โดยใช้ไลบรารีการออกแบบวัสดุเชิงมุมอย่างเป็นทางการ เราจะสร้างเว็บแอปพลิเคชันตัวสร้างรหัส QR โดยอิงจาก Angular ในขณะที่โฮสต์บน Netlify
ไฟล์สำหรับบทช่วยสอนนี้สามารถพบได้บน GitHub และมีการปรับใช้เวอร์ชันสาธิตที่นี่
เริ่มต้น
- ติดตั้งเชิงมุม 8,
- สร้างบัญชี GitHub
- ติดตั้ง Git บนคอมพิวเตอร์ของคุณ
- สร้างบัญชี Netlify
หมายเหตุ : ฉันจะใช้ VSCode และ Microsoft Windows เป็น IDE และ OS ที่ต้องการ แม้ว่าขั้นตอนจะคล้ายกันสำหรับ IDE อื่นๆ บนระบบปฏิบัติการอื่น
หลังจากข้อกำหนดเบื้องต้นข้างต้นเสร็จแล้ว มาเริ่มกันเลย!
การเยาะเย้ยและการวางแผน
ก่อนที่เราจะเริ่มสร้างโครงการ การวางแผนล่วงหน้าน่าจะเป็นประโยชน์: เราต้องการ UI แบบใดในแอปพลิเคชันของเรา จะมีชิ้นส่วนที่นำกลับมาใช้ใหม่ได้หรือไม่? แอปพลิเคชันจะโต้ตอบกับบริการภายนอกอย่างไร
ก่อนอื่น ตรวจสอบ UI จำลอง
นี่คือหน้าที่แตกต่างกันสามหน้าซึ่งจะอยู่ในแอปพลิเคชัน หน้าแรกจะเป็นจุดเริ่มต้นของการสมัครของเรา การสร้างหน้า QR ควรจัดการกับการสร้างรหัส QR ใหม่ หน้าประวัติจะแสดงรหัส QR ที่บันทึกไว้ทั้งหมด
ม็อคอัพไม่เพียงแต่ให้แนวคิดเกี่ยวกับรูปลักษณ์ของแอปพลิเคชันเท่านั้น แต่ยังแบ่งความรับผิดชอบของแต่ละหน้าด้วย
ข้อสังเกตอย่างหนึ่ง (จากการล้อเลียน) ก็คือ ดูเหมือนว่าแถบการนำทางด้านบนจะพบได้ทั่วไปในทุกหน้า ดังนั้น สามารถสร้างแถบนำทางเป็นส่วนประกอบที่นำกลับมาใช้ใหม่ได้และนำกลับมาใช้ใหม่ได้
ตอนนี้เรามีแนวคิดพอสมควรแล้วว่าแอปพลิเคชันจะมีหน้าตาเป็นอย่างไรและสามารถนำกลับมาใช้ใหม่ได้อย่างไร มาเริ่มกันเลย
การสร้างโครงการเชิงมุมใหม่
เปิด VSCode จากนั้นเปิดหน้าต่างเทอร์มินัลใน VSCode เพื่อสร้างโปรเจ็กต์ Angular ใหม่
เทอร์มินัลจะเปิดขึ้นด้วยเส้นทางเริ่มต้นตามที่แสดงในข้อความแจ้ง คุณสามารถเปลี่ยนเป็นไดเร็กทอรีที่ต้องการก่อนดำเนินการต่อ ในกรณีของ Windows ฉันจะใช้คำสั่ง cd
ก้าวไปข้างหน้า angular-cli มีคำสั่งให้สร้างโครงการ ng new <project-name>
เพียงใช้ชื่อโครงการแฟนซีที่คุณชอบแล้วกด Enter เช่น ng new qr
สิ่งนี้จะกระตุ้นเวทย์มนตร์เชิงมุม จะมีตัวเลือกบางอย่างในการกำหนดค่าบางแง่มุมของโครงการ เช่น การเพิ่มการกำหนดเส้นทางเชิงมุม จากนั้น ตามตัวเลือกที่เลือก มันจะสร้างโครงร่างโครงการทั้งหมด ซึ่งสามารถรันได้โดยไม่ต้องดัดแปลงใดๆ
สำหรับบทช่วยสอนนี้ ให้ป้อน ใช่ สำหรับการกำหนดเส้นทางและเลือก CSS สำหรับการกำหนดสไตล์ สิ่งนี้จะสร้างโปรเจ็กต์ Angular ใหม่:
ตอนนี้เรามีโครงการ Angular ที่ทำงานอย่างเต็มที่แล้ว เพื่อให้แน่ใจว่าทุกอย่างทำงานอย่างถูกต้อง เราสามารถเรียกใช้โครงการโดยป้อนคำสั่งนี้ในเทอร์มินัล: ng serve
เอ่อ แต่เดี๋ยวก่อน ผลลัพธ์นี้มีข้อผิดพลาด อะไรจะเกิดขึ้น?
ไม่ต้องกังวล เมื่อใดก็ตามที่คุณสร้างโปรเจ็กต์ใหม่โดยใช้ angular-cli โปรเจ็กต์จะสร้างโครงกระดูกทั้งหมดภายในโฟลเดอร์ที่ตั้งชื่อตามชื่อโปรเจ็กต์ที่ระบุในคำสั่ง ng new qr
ที่นี่เราจะต้องเปลี่ยนไดเร็กทอรีการทำงานปัจจุบันเป็นไดเร็กทอรีที่เพิ่งสร้างขึ้น ใน Windows ใช้คำสั่ง cd qr
เพื่อเปลี่ยนไดเร็กทอรี
ตอนนี้ให้ลองเรียกใช้โครงการอีกครั้งด้วยความช่วยเหลือของ ng serve
:
เปิดเว็บเบราว์เซอร์ ไปที่ URL https://localhost:4200 เพื่อดูโครงการที่กำลังดำเนินการ คำสั่ง ng serve
รันแอปพลิเคชันบนพอร์ต 4200 โดยค่าเริ่มต้น
เคล็ดลับ : ในการรันบนพอร์ตอื่น เราใช้คำสั่ง ng serve --port <any-port>
ng serve --port ng serve --port 3000
สิ่งนี้ทำให้มั่นใจได้ว่าโปรเจ็กต์ Angular พื้นฐานของเรากำลังดำเนินการอยู่ ไปต่อกันเลย
เราจำเป็นต้องเพิ่มโฟลเดอร์โครงการใน VSCode ไปที่เมนู "ไฟล์" และเลือก "เปิดโฟลเดอร์" และเลือกโฟลเดอร์โครงการ โฟลเดอร์โปรเจ็กต์จะแสดงในมุมมอง Explorer ทางด้านซ้าย
การเพิ่มไลบรารีวัสดุเชิงมุม
ในการติดตั้งไลบรารีวัสดุเชิงมุม ให้ใช้คำสั่งต่อไปนี้ในหน้าต่างเทอร์มินัล: ng add @angular/material
สิ่งนี้จะ (อีกครั้ง) จะถามคำถามบางอย่าง เช่น ธีมที่คุณต้องการ คุณต้องการให้แอนิเมชั่นเริ่มต้นหรือไม่ จำเป็นต้องมีการรองรับการสัมผัสหรือไม่ เป็นต้น เราจะเลือกธีม Indigo/Pink
เริ่มต้น Yes
เพื่อเพิ่มไลบรารี HammerJS
และภาพเคลื่อนไหวของเบราว์เซอร์
คำสั่งดังกล่าวยังกำหนดค่าทั้งโปรเจ็กต์เพื่อรองรับส่วนประกอบวัสดุ
- มันเพิ่มการพึ่งพาโครงการใน package.json
- มันเพิ่มฟอนต์ Roboto ให้กับไฟล์ index.html
- เพิ่มแบบอักษรไอคอนดีไซน์ Material ให้กับ index.html ของคุณ
- นอกจากนี้ยังเพิ่มสไตล์ CSS ทั่วโลกบางส่วนให้กับ:
- ลบขอบออกจากร่างกาย,
- กำหนด
height: 100%
เป็น HTML และเนื้อหา - ตั้งค่า Roboto เป็นแบบอักษรเริ่มต้นของแอปพลิเคชัน
เพียงเพื่อให้แน่ใจว่าทุกอย่างเรียบร้อยดี คุณสามารถเรียกใช้โครงการได้อีกครั้ง ณ จุดนี้ แม้ว่าคุณจะไม่สังเกตเห็นสิ่งใหม่
การเพิ่มหน้าแรก
โครงงานของเราพร้อมแล้ว เริ่มต้นด้วยการเพิ่มหน้าแรก
เราต้องการให้หน้าแรกของเราเรียบง่ายเหมือนภาพด้านบน หน้าแรกนี้ใช้ส่วนประกอบวัสดุเชิงมุมบางส่วน มาผ่ากัน
- แถบด้านบนเป็นองค์ประกอบ
nav
ทาง HTML อย่างง่ายซึ่งมีปุ่มสไตล์วัสดุ ปุ่มmat-button
พร้อมรูปภาพและข้อความเป็นลูก สีของแถบจะเหมือนกับสีหลักที่เลือกในขณะที่เพิ่มไลบรารีวัสดุเชิงมุม - ภาพตรงกลาง;
- อีกปุ่มหนึ่งคือ
mat-button
โดยมีข้อความเป็นลูก ปุ่มนี้จะอนุญาตให้ผู้ใช้นำทางไปยังหน้าประวัติ; - ป้ายการนับ
matBadge
แนบกับปุ่มด้านบนแสดงจำนวนรหัส QR ที่ผู้ใช้บันทึกไว้ - ปุ่มการทำงานแบบลอย
mat-fab
ที่มุมล่างขวาพร้อมสีเฉพาะจุดจากธีมที่เลือก
พูดนอกเรื่องเล็กน้อย มาเพิ่มส่วนประกอบและบริการที่จำเป็นอื่น ๆ ก่อน
การเพิ่มส่วนหัว
ตามที่วางแผนไว้ก่อนหน้านี้ ควรใช้แถบนำทางซ้ำ มาสร้างเป็นส่วนประกอบเชิงมุมแยกกัน เปิดเทอร์มินัลใน VSCode แล้วพิมพ์ ng gc header
(ย่อมาจาก ng create component header) แล้วกด Enter นี้จะสร้างโฟลเดอร์ใหม่ชื่อ "ส่วนหัว" ซึ่งจะมีสี่ไฟล์:
- header.component.css : ใช้เพื่อจัดเตรียมสไตล์สำหรับส่วนประกอบนี้
- header.component.html : สำหรับเพิ่มองค์ประกอบ HTML
- header.component.spec.ts : สำหรับเขียนกรณีทดสอบ
- header.component.ts : เพื่อเพิ่มตรรกะตาม typescript
ในการทำให้ส่วนหัวดูเหมือนกับใน mocks ให้เพิ่ม HTML ด้านล่างใน header.component.html :
<nav class="navbar" [class.mat-elevation-z8]=true> <div> <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/"> <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon> </button> <span>{{currentTitle}}</span> </div> <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button"> <img src="../../assets/qr-icon-white.png"> <span>QR Generator</span> </button> <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history"> <span>History</span> </button> </nav>
เคล็ดลับ : หากต้องการเพิ่มระดับความสูงสำหรับส่วนประกอบวัสดุใด ๆ ให้ใช้ [class.mat-elevation-z8]=true
ค่าระดับความสูงสามารถเปลี่ยนแปลงได้โดยการเปลี่ยน ค่า z ในกรณีนี้คือ z8
ตัวอย่างเช่น หากต้องการเปลี่ยนระดับความสูงเป็น 16 ให้ใช้ [class.mat-elevation-z16]=true
ในข้อมูลโค้ด HTML ข้างต้น มีองค์ประกอบวัสดุเชิงมุมที่ใช้อยู่สององค์ประกอบ: mat-icon
และ mat-button/mat-icon-button
การใช้งานนั้นง่ายมาก ขั้นแรก เราต้องเพิ่มทั้งสองเป็นโมดูลใน app.module.ts ของเราดังที่แสดงด้านล่าง:
ซึ่งจะช่วยให้เราใช้องค์ประกอบวัสดุเชิงมุมทั้งสองนี้ได้ทุกที่ในองค์ประกอบใดๆ
สำหรับการเพิ่มปุ่มวัสดุ จะใช้ข้อมูลโค้ด HTML ต่อไปนี้:
<button mat-button> Material Button </button>
มีองค์ประกอบปุ่มวัสดุประเภทต่างๆ ที่มีอยู่ในไลบรารีวัสดุเชิงมุม เช่น mat-raised-button
mat-flat-button
mat-fab
อื่นๆ เพียงแทนที่ mat-button
ในข้อมูลโค้ดด้านบนด้วยประเภทอื่น
องค์ประกอบอื่นคือ mat-icon
ซึ่งใช้เพื่อแสดงไอคอนที่มีอยู่ในไลบรารีไอคอนวัสดุ เมื่อมีการเพิ่มไลบรารี่วัสดุเชิงมุมในตอนเริ่มต้น การอ้างอิงถึงไลบรารีไอคอนวัสดุก็ถูกเพิ่มเข้าไปด้วย ซึ่งทำให้เราสามารถใช้ไอคอนจากไอคอนมากมาย
การใช้งานนั้นง่ายเหมือน:
<mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon>
แท็ก <i>
ที่ซ้อนกันสามารถใช้เพื่อเปลี่ยนขนาดไอคอน (นี่คือ md-32
) ซึ่งจะทำให้ไอคอนมีขนาดความสูงและความกว้าง 32px ค่านี้สามารถเป็น md-24
, md-48
เป็นต้น ค่าของแท็ก <i>
ที่ซ้อนกันคือชื่อของไอคอน (ชื่อสามารถพบได้ที่นี่สำหรับไอคอนอื่น ๆ )
การช่วยสำหรับการเข้าถึง
เมื่อใดก็ตามที่มีการใช้ไอคอนหรือรูปภาพ จำเป็นต้องให้ข้อมูลที่เพียงพอสำหรับวัตถุประสงค์ในการเข้าถึงหรือสำหรับผู้ใช้โปรแกรมอ่านหน้าจอ ARIA (Accessible Rich Internet Applications) กำหนดวิธีการทำให้เนื้อหาเว็บและแอปพลิเคชันเว็บสามารถเข้าถึงได้มากขึ้นสำหรับผู้ทุพพลภาพ
จุดหนึ่งที่ควรทราบคือองค์ประกอบ HTML ที่มีความหมายดั้งเดิม (เช่น nav
) ไม่ต้องการแอตทริบิวต์ ARIA; โปรแกรมอ่านหน้าจอจะรู้อยู่แล้วว่า nav
เป็นองค์ประกอบการนำทางและอ่านในลักษณะนี้
ข้อกำหนด ARIA แบ่งออกเป็นสามประเภท: บทบาท สถานะ และคุณสมบัติ สมมติว่ามีการใช้ div
เพื่อสร้างแถบความคืบหน้าในโค้ด HTML ไม่มีความหมายดั้งเดิม บทบาท ARIA สามารถอธิบายวิดเจ็ตนี้เป็นแถบความคืบหน้า คุณสมบัติ ARIA สามารถแสดงถึงคุณลักษณะของมันได้ เช่น สามารถลากได้ สถานะ ARIA จะอธิบายสถานะปัจจุบัน เช่น ค่าปัจจุบันของแถบความคืบหน้า ดูตัวอย่างด้านล่าง:
<div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>
ในทำนองเดียวกัน แอตทริบิวต์ aria ที่ใช้บ่อยมาก: aria-hidden=true/false
ถูกใช้ ค่า true ทำให้องค์ประกอบนั้นไม่ปรากฏแก่โปรแกรมอ่านหน้าจอ
เนื่องจากองค์ประกอบ UI ส่วนใหญ่ที่ใช้ในแอปพลิเคชันนี้มีความหมายเชิงความหมายดั้งเดิม แอตทริบิวต์ ARIA เดียวที่ใช้คือระบุสถานะการมองเห็น ARIA สำหรับข้อมูลโดยละเอียด ให้อ้างอิงถึงสิ่งนี้
header.component.html มีตรรกะบางอย่างในการซ่อนและแสดงปุ่มย้อนกลับขึ้นอยู่กับหน้าปัจจุบัน นอกจากนี้ ปุ่มโฮมยังมีรูปภาพ/โลโก้ซึ่งควรเพิ่มในโฟลเดอร์ /assets
ดาวน์โหลดรูปภาพจากที่นี่และบันทึกไว้ในโฟลเดอร์ /assets
สำหรับการกำหนดสไตล์ของแถบนำทาง ให้เพิ่ม css ด้านล่างใน header.component.css :
.navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 2; background: #3f51b5; display: flex; flex-wrap: wrap; align-items: center; padding: 12px 16px; } .button { color: white; margin: 0px 10px; }
เนื่องจากเราต้องการให้องค์ประกอบส่วนหัวสามารถนำมาใช้ซ้ำได้กับส่วนประกอบอื่นๆ ดังนั้นในการตัดสินใจเลือกสิ่งที่ควรแสดง เราจึงต้องการสิ่งเหล่านั้นเป็นพารามิเตอร์จากส่วนประกอบอื่นๆ สิ่งนี้ต้องใช้ @Input()
มัณฑนากรซึ่งจะผูกกับตัวแปรที่เราใช้ใน header.component.html
เพิ่มบรรทัดเหล่านี้ในไฟล์ header.component.ts :
// Add these three lines above the constructor entry. @Input() showBackButton: boolean; @Input() currentTitle: string; @Input() showHistoryNav: boolean; constructor() { }
การเชื่อมโยงสามรายการข้างต้นจะถูกส่งผ่านเป็นพารามิเตอร์จากส่วนประกอบอื่นซึ่งส่วนประกอบส่วนหัวจะใช้ การใช้งานจะชัดเจนขึ้นเมื่อเราก้าวไปข้างหน้า
ต่อไปเราต้องสร้างหน้าแรกที่สามารถแสดงด้วยองค์ประกอบเชิงมุม เริ่มต้นด้วยการสร้างองค์ประกอบอื่น พิมพ์ ng gc home
ในเทอร์มินัลเพื่อสร้างส่วนประกอบหลักโดยอัตโนมัติ เช่นเดียวกับก่อนหน้านี้ โฟลเดอร์ใหม่ชื่อ “home” จะถูกสร้างขึ้นโดยมีไฟล์สี่ไฟล์ที่แตกต่างกัน ก่อนดำเนินการแก้ไขไฟล์เหล่านั้น ให้เพิ่มข้อมูลการกำหนดเส้นทางไปยังโมดูลการกำหนดเส้นทางเชิงมุม
การเพิ่มเส้นทาง
Angular ให้วิธีการจับคู่ URL กับองค์ประกอบเฉพาะ เมื่อใดก็ตามที่การนำทางเกิดขึ้น Angular framework จะตรวจสอบ URL และตามข้อมูลที่มีอยู่ในไฟล์ app-routing.module.ts มันเริ่มต้นองค์ประกอบที่แมป วิธีนี้ทำให้ส่วนประกอบต่างๆ ไม่จำเป็นต้องแบกรับความรับผิดชอบในการเริ่มต้นส่วนประกอบอื่นๆ ในกรณีของเรา แอปพลิเคชันสามารถนำทางได้สามหน้าโดยคลิกที่ปุ่มต่างๆ เราบรรลุสิ่งนี้โดยใช้ประโยชน์จากการสนับสนุนการกำหนดเส้นทางที่จัดทำโดยกรอบงานเชิงมุม
องค์ประกอบหลักควรเป็นจุดเริ่มต้นของแอปพลิเคชัน มาเพิ่มข้อมูลนี้ในไฟล์ app-routing.module.ts
คุณสมบัติ path
ถูกตั้งค่าเป็นสตริงว่าง ทำให้เราสามารถจับคู่ URL ของแอปพลิเคชันกับองค์ประกอบหน้าแรก เช่น google.com
ซึ่งแสดงหน้าแรกของ Google
เคล็ดลับ : ค่าพาธไม่เคยขึ้นต้นด้วย “ /
” แต่จะใช้สตริงว่างแทน แม้ว่าเส้นทางจะเหมือนกับ search/coffee
ย้ายกลับไปที่องค์ประกอบหน้าแรก แทนที่เนื้อหาของ home.component.html ด้วยสิ่งนี้:
<app-header [showBackButton]="false" [currentTitle]=""></app-header> <app-profile></app-profile> <!-- FAB Fixed --> <button mat-fab class="fab-bottom-right" routerLink="/create"> <mat-icon> <i class="material-icons md-48">add</i> </mat-icon> </button>
ส่วนประกอบภายในบ้านมีสามส่วน:
- องค์ประกอบส่วนหัวที่ใช้ซ้ำได้
<app-header>
, - องค์ประกอบโปรไฟล์
<app-profile>
, - ปุ่มการทำงานแบบลอยตัวที่ด้านล่างขวา
ข้อมูลโค้ด HTML ด้านบนแสดงวิธีการใช้ส่วนประกอบส่วนหัวที่นำกลับมาใช้ใหม่ได้ในส่วนประกอบอื่นๆ เราเพียงแค่ใช้ตัวเลือกส่วนประกอบและส่งผ่านพารามิเตอร์ที่จำเป็น
ส่วนประกอบโปรไฟล์ถูกสร้างขึ้นเพื่อใช้เป็นเนื้อหาสำหรับหน้าแรก — เราจะสร้างมันขึ้นมาในไม่ช้า
ปุ่มการทำงานแบบลอยพร้อมไอคอน +
เป็นปุ่มวัสดุเชิงมุมประเภท mat-fab
ที่ด้านล่างขวาของหน้าจอ มีคำสั่งแอตทริบิวต์ routerLink
ซึ่งใช้ข้อมูลเส้นทางที่ให้ไว้ใน app-routing.module.ts
สำหรับการนำทาง ในกรณีนี้ ปุ่มมีค่าเส้นทางเป็น /create ซึ่งจะถูกแมปเพื่อสร้างส่วนประกอบ
หากต้องการให้ปุ่มสร้างลอยอยู่ที่ด้านล่างขวา ให้เพิ่มโค้ด CSS ด้านล่างใน home.component.css :
.fab-bottom-right { position: fixed; left: auto; bottom: 5%; right: 10%; }
เนื่องจากองค์ประกอบโปรไฟล์ควรจะจัดการเนื้อหาของหน้าแรก เราจะปล่อยให้ home.component.ts
ไม่เสียหาย
การเพิ่มส่วนประกอบโปรไฟล์
เปิดเทอร์มินัล พิมพ์ ng gc profile
แล้วกด Enter เพื่อสร้างองค์ประกอบโปรไฟล์ ตามที่วางแผนไว้ก่อนหน้านี้ ส่วนประกอบนี้จะจัดการเนื้อหาหลักของโฮมเพจ เปิด profile.component.html
และแทนที่เนื้อหาด้วยสิ่งนี้:
<div class="center profile-child"> <img class="avatar" src="../../assets/avatar.png"> <div class="profile-actions"> <button mat-raised-button matBadge="{{historyCount}}" matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent" color="primary" routerLink="/history"> <span>History</span> </button> </div> </div>
ตัวอย่าง HTML ด้านบนแสดงวิธีใช้องค์ประกอบ matBadge
ของไลบรารีวัสดุ เพื่อให้สามารถใช้งานได้ที่นี่ เราต้องปฏิบัติตามขั้นตอนปกติของการเพิ่ม MatBadgeModule
ลงในไฟล์ app.module.ts
ป้ายเป็นตัวบอกสถานะภาพขนาดเล็กสำหรับองค์ประกอบ UI เช่น ปุ่มหรือไอคอนหรือข้อความ ในกรณีนี้ ใช้กับปุ่มเพื่อแสดงจำนวน QR ที่ผู้ใช้บันทึกไว้ ป้ายไลบรารีวัสดุเชิงมุมมีคุณสมบัติอื่นๆ มากมาย เช่น การตั้งค่าตำแหน่งของป้ายด้วย matBadgePosition
, matBadgeSize
เพื่อระบุขนาด และ matBadgeColor
เพื่อกำหนดสีป้าย
ต้องเพิ่มเนื้อหารูปภาพอีกหนึ่งรายการในโฟลเดอร์เนื้อหา: ดาวน์โหลด บันทึกเดียวกันไปยังโฟลเดอร์ /assets
ของโครงการ
เปิด profile.component.css และเพิ่มสิ่งนี้:
.center { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); } .profile-child { display: flex; flex-direction: column; align-items: center; } .profile-actions { padding-top: 20px; } .avatar { border-radius: 50%; width: 180px; height: 180px; }
CSS ข้างต้นจะบรรลุ UI ตามที่วางแผนไว้
ต่อไป เราต้องการตรรกะบางอย่างในการอัปเดตค่าการนับประวัติเนื่องจากจะมีผลใน matBadge
ที่ใช้ก่อนหน้านี้ เปิด profile.component.ts และเพิ่มข้อมูลโค้ดต่อไปนี้อย่างเหมาะสม:
export class ProfileComponent implements OnInit { historyCount = 0; constructor(private storageUtilService: StorageutilService) { } ngOnInit() { this.updateHistoryCount(); } updateHistoryCount() { this.historyCount = this.storageUtilService.getHistoryCount(); } }
เราได้เพิ่ม StorageutilService แล้ว แต่ยังไม่ได้สร้างบริการดังกล่าวมาจนถึงตอนนี้ เพิกเฉยต่อข้อผิดพลาด เราได้เสร็จสิ้นองค์ประกอบโปรไฟล์ของเรา ซึ่งก็เสร็จสิ้นองค์ประกอบหน้าแรกของเราด้วย เราจะทบทวนองค์ประกอบโปรไฟล์นี้อีกครั้งหลังจากสร้างบริการยูทิลิตี้การจัดเก็บข้อมูลของเรา โอเค งั้นเรามาทำกัน
ที่เก็บข้อมูลในเครื่อง
HTML5 มีคุณสมบัติการจัดเก็บเว็บซึ่งสามารถใช้เพื่อจัดเก็บข้อมูลในเครื่อง ซึ่งให้พื้นที่จัดเก็บมากขึ้นเมื่อเทียบกับคุกกี้ — อย่างน้อย 5MB เทียบกับ 4KB ที่จัดเก็บเว็บมีสองประเภทที่มีขอบเขตและอายุการใช้งานต่างกัน: Local และ Session อดีตสามารถจัดเก็บข้อมูลอย่างถาวรในขณะที่หลังเป็นแบบชั่วคราวและสำหรับเซสชันเดียว การตัดสินใจเลือกประเภทจะขึ้นอยู่กับกรณีการใช้งาน ในสถานการณ์ของเรา เราต้องการบันทึกข้ามเซสชัน ดังนั้นเราจะใช้ที่ เก็บ ข้อมูลในเครื่อง
ข้อมูลแต่ละชิ้นถูกจัดเก็บไว้ในคู่คีย์/ค่า เราจะใช้ข้อความที่สร้าง QR เป็นคีย์และรูปภาพ QR เข้ารหัสเป็นสตริง base64 เป็นค่า สร้างโฟลเดอร์เอนทิตี ภายในโฟลเดอร์สร้างไฟล์ qr-object.ts ใหม่และเพิ่มข้อมูลโค้ดตามที่แสดง:
เนื้อหาของชั้นเรียน:
export class QR { text: string; imageBase64: string; constructor(text: string, imageBase64: string) { this.imageBase64 = imageBase64; this.text = text; } }
เมื่อใดก็ตามที่ผู้ใช้บันทึก QR ที่สร้างขึ้น เราจะสร้างวัตถุของคลาสข้างต้นและบันทึกวัตถุนั้นโดยใช้บริการยูทิลิตี้การจัดเก็บ
สร้างโฟลเดอร์บริการใหม่ เราจะสร้างบริการมากมาย ดีกว่าที่จะรวมกลุ่มเข้าด้วยกัน
เปลี่ยนไดเร็กทอรีการทำงานปัจจุบันเป็น services, cd services
เพื่อสร้างบริการใหม่ ng gs <any name>
นี่เป็นชวเลขเพื่อ ng generate service <any name>
พิมพ์ ng gs storageutil
แล้วกด enter
สิ่งนี้จะสร้างสองไฟล์:
- storageutil.service.ts
- storageutil.service.spec.ts
ส่วนหลังสำหรับการเขียนแบบทดสอบหน่วย เปิด storageutil.service.ts และเพิ่มสิ่งนี้:
private historyCount: number; constructor() { } saveHistory(key : string, item :string) { localStorage.setItem(key, item) this.historyCount = this.historyCount + 1; } readHistory(key : string) : string { return localStorage.getItem(key) } readAllHistory() : Array<QR> { const qrList = new Array<QR>(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); if (key && value) { const qr = new QR(key, value); qrList.push(qr); } } this.historyCount = qrList.length; return qrList; } getHistoryCount(): number { if (this.historyCount) { return this.historyCount; } this.readAllHistory(); return this.historyCount; } deleteHistory(key : string) { localStorage.removeItem(key) this.historyCount = this.historyCount - 1; }
นำเข้าคลาส qr-object เพื่อแก้ไขข้อผิดพลาด ในการใช้คุณสมบัติการจัดเก็บในเครื่อง ไม่จำเป็นต้องนำเข้าสิ่งใหม่ เพียงแค่ใช้คำสำคัญ localStorage
เพื่อบันทึกหรือรับค่าตามคีย์
ตอนนี้เปิดไฟล์ profile.component.ts อีกครั้งและนำเข้าคลาส StorageutilService
เพื่อปิดองค์ประกอบโปรไฟล์อย่างเหมาะสม
ดำเนินโครงการเราจะเห็นว่าหน้าแรกเป็นไปตามแผนที่วางไว้
การเพิ่มสร้างหน้า QR
เรามีหน้าแรกพร้อมแล้ว แม้ว่าปุ่มสร้าง/เพิ่มจะไม่ทำอะไรเลย ไม่ต้องกังวล ตรรกะที่แท้จริงถูกเขียนไว้แล้ว เราใช้คำสั่ง routerLink
เพื่อเปลี่ยนเส้นทางพื้นฐานของ URL เป็น /create
แต่ไม่มีการเพิ่มการแมปในไฟล์ app-routing.module.ts
มาสร้างองค์ประกอบที่จะจัดการกับการสร้างรหัส QR ใหม่ พิมพ์ ng gc create-qr
แล้วกด Enter เพื่อสร้างองค์ประกอบใหม่
เปิดไฟล์ app-routing.module.ts และเพิ่มรายการด้านล่างในอาร์เรย์ routes
:
{ path: 'create', component: CreateQrComponent },
สิ่งนี้จะจับ CreateQRComponent
กับ URL /create
เปิด create-qr.components.html และแทนที่เนื้อหาด้วยสิ่งนี้:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <mat-card class="qrCard" [class.mat-elevation-z12]=true> <div class="qrContent"> <!--Close button section--> <div class="closeBtn"> <button mat-icon-button color="accent" routerLink="/" matTooltip="Close"> <mat-icon> <i class="material-icons md-48">close</i> </mat-icon> </button> </div> <!--QR code image section--> <div class="qrImgDiv"> <img *ngIf="!showProgressSpinner" src={{qrCodeImage}} width="200px" height="200px"> <mat-spinner *ngIf="showProgressSpinner"></mat-spinner> <div class="actionButtons" *ngIf="!showProgressSpinner"> <button mat-icon-button color="accent" matTooltip="Share this QR"> <mat-icon> <i class="material-icons md-48">share</i> </mat-icon> </button> <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR"> <mat-icon> <i class="material-icons md-48">save</i> </mat-icon> </button> </div> </div> <!--Textarea to write any text or link--> <div class="qrTextAreaDiv"> <mat-form-field> <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4" placeholder="Enter a website link or any text..."></textarea> </mat-form-field> </div> <!--Create Button--> <div class="createBtnDiv"> <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above" (click)="createQrCode()">Create</button> </div> </div> </mat-card>
ตัวอย่างข้างต้นใช้องค์ประกอบไลบรารีวัสดุเชิงมุมจำนวนมาก ตามที่วางแผนไว้ มีการอ้างอิงส่วนประกอบส่วนหัวหนึ่งรายการซึ่งจะส่งผ่านพารามิเตอร์ที่จำเป็น ถัดลงมาเป็นส่วนหลักของหน้าสร้าง ประกอบด้วยการ์ดวัสดุเชิงมุมหนึ่งใบหรือการ์ด mat-card
ตรงกลางและยกระดับสูงสุด 12px เนื่องจาก [class.mat-elevation-z12]=true
การ์ดวัสดุเป็นเพียงคอนเทนเนอร์ประเภทอื่นที่สามารถใช้เป็นแท็ก div
อื่นๆ ได้ แม้ว่าไลบรารี่วัสดุจะมีคุณสมบัติบางอย่างในการจัดวางข้อมูลที่ชัดเจนใน mat-card
เช่น ตำแหน่งรูปภาพ ชื่อ คำบรรยาย คำอธิบาย และการดำเนินการดังที่แสดงด้านล่าง
ในข้อมูลโค้ด HTML ข้างต้น เราได้ใช้ mat-card
เช่นเดียวกับคอนเทนเนอร์อื่นๆ องค์ประกอบไลบรารีวัสดุอื่นที่ใช้คือ matTooltip
; มันเป็นเพียงคำแนะนำเครื่องมืออื่นที่ใช้งานง่าย แสดงเมื่อผู้ใช้วางเมาส์เหนือหรือกดองค์ประกอบค้างไว้ เพียงใช้ข้อมูลโค้ดด้านล่างเพื่อแสดงคำแนะนำเครื่องมือ:
matTooltip="Any text you want to show"
สามารถใช้กับปุ่มไอคอนหรือองค์ประกอบ UI อื่น ๆ เพื่อถ่ายทอดข้อมูลเพิ่มเติม ในบริบทของแอปพลิเคชัน จะแสดงข้อมูลเกี่ยวกับปุ่มไอคอนปิด ในการเปลี่ยนตำแหน่งของคำแนะนำเครื่องมือ ให้ใช้ matTooltipPosition
:
matTooltip="Any text you want to show" matTooltipPosition="above"
นอกจาก matTooltip
แล้ว mat-spinner
ยังใช้เพื่อแสดงความคืบหน้าในการโหลด เมื่อผู้ใช้คลิกที่ปุ่ม "สร้าง" จะมีการโทรผ่านเครือข่าย นี่คือเวลาที่สปินเนอร์แสดงความคืบหน้า เมื่อการโทรกลับเครือข่ายพร้อมผลลัพธ์ เราเพียงแค่ซ่อนสปินเนอร์ ใช้งานได้ง่ายๆ ดังนี้
<mat-spinner *ngIf="showProgressSpinner"></mat-spinner>
showProgressSpinner
เป็นตัวแปรบูลีนที่ใช้เพื่อแสดง/ซ่อนสปินเนอร์ความคืบหน้า ไลบรารียังมีพารามิเตอร์อื่นๆ เช่น [color]='accent'
เพื่อเปลี่ยนสี [mode]='indeterminate'
เพื่อเปลี่ยนประเภทสปินเนอร์ความคืบหน้า ตัวหมุนความคืบหน้าที่ไม่แน่นอนจะไม่แสดงความคืบหน้าของงานในขณะที่ตัวกำหนดหนึ่งตัวสามารถมีค่าต่างกันเพื่อสะท้อนถึงความคืบหน้าของงาน ที่นี่ใช้สปินเนอร์ที่ไม่แน่นอนเนื่องจากเราไม่ทราบว่าการโทรเครือข่ายจะใช้เวลานานแค่ไหน
ไลบรารีวัสดุจัดเตรียม textarea ที่หลากหลายซึ่งสอดคล้องกับแนวทางวัสดุ แต่สามารถใช้ได้เฉพาะในฐานะที่สืบทอดมาจาก mat-form-field
การใช้ textarea ของวัสดุนั้นง่ายพอๆ กับ HTML เริ่มต้น ดังตัวอย่างด้านล่าง:
<mat-form-field> <textarea matInput placeholder="Hint text"></textarea> </mat-form-field>
matInput
เป็นคำสั่งที่อนุญาตให้แท็ก input
ดั้งเดิมทำงานกับ mat-form-field
คุณสมบัติ placeholder
อนุญาตให้เพิ่มข้อความคำใบ้สำหรับผู้ใช้
เคล็ดลับ : ใช้ คุณสมบัติ cdkTextareaAutosize
textarea เพื่อให้ปรับขนาดได้อัตโนมัติ ใช้ cdkAutosizeMinRows
และ cdkAutosizeMaxRows
เพื่อตั้งค่าแถวและคอลัมน์ และทั้งสามเข้าด้วยกันเพื่อทำให้ textarea ปรับขนาดอัตโนมัติจนถึงขีดจำกัดสูงสุดของแถวและคอลัมน์ที่ตั้งไว้
ในการใช้องค์ประกอบไลบรารีวัสดุเหล่านี้ เราจำเป็นต้องเพิ่มองค์ประกอบเหล่านี้ในไฟล์ app.module.ts
มีภาพตัวแทนที่ใช้ใน HTML ดาวน์โหลดและบันทึกลงในโฟลเดอร์ /assets
นอกจากนี้ HTML ข้างต้นยังต้องการการจัดรูปแบบ CSS ดังนั้นให้เปิดไฟล์ create-qr.component.ts และเพิ่มสิ่งต่อไปนี้:
.qrCard { display: flex; flex-direction: column; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20%; height: 65%; padding: 50px 20px; } .qrContent { display: flex; flex-direction: column; align-items: center; width: 100%; } .qrTextAreaDiv { width: 100%; display: flex; flex-direction: row; justify-content: center; padding: 0px 0px; position: absolute; bottom: 10%; } .createBtn { left: 50%; transform: translate(-50%, 0px); width: 80%; } .createBtnDiv { position: absolute; bottom: 5%; width: 100%; } .closeBtn { display: flex; flex-direction: row-reverse; align-items: flex-end; width: 100%; margin-bottom: 20px; } .closeBtnFont { font-size: 32px; color: rgba(0,0,0,0.75); } .qrImgDiv { top: 20%; position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; } .actionButtons { display: flex; flex-direction: row; padding-top: 20px; }
มาเชื่อมโยง UI กับตรรกะกัน เปิดไฟล์ create-qr.component.ts และเพิ่มโค้ดด้านล่าง โดยปล่อยให้บรรทัดที่มีอยู่แล้ว:
export class CreateQrComponent implements OnInit { qrCodeImage = '../../../assets/download.png'; showProgressSpinner = false; qrText: string; currentQR; showBackButton = true; title = 'Generate New QR Code'; showHistoryNav = true; constructor(private snackBar: MatSnackBar, private restutil: RestutilService, private storageService: StorageutilService) { } ngOnInit() { } createQrCode() { //Check if any value is given for the qr code text if (!!this.qrText) { //Make the http call to load qr code this.loadQRCodeImage(this.qrText); } else { //Show snackbar this.showSnackbar('Enter some text first') } } public loadQRCodeImage(text: string) { // Show progress spinner as the request is being made this.showProgressSpinner = true; // Trigger the API call this.restutil.getQRCode(text).subscribe(image =>{ // Received the result - as an image blob - require parsing this.createImageBlob(image); }, error => { console.log('Cannot fetch QR code from the url', error) // Hide the spinner - show a proper error message this.showProgressSpinner = false; }); } private createImageBlob(image: Blob) { // Create a file reader to read the image blob const reader = new FileReader(); // Add event listener for "load" - invoked once the blob reading is complete reader.addEventListener('load', () => { this.qrCodeImage = reader.result.toString(); //Hide the progress spinner this.showProgressSpinner = false; this.currentQR = reader.result.toString(); }, false); // Read image blob if it is not null or undefined if (image) { reader.readAsDataURL(image); } } saveQR() { if (!!this.qrText) { this.storageService.saveHistory(this.qrText, this.currentQR); this.showSnackbar('QR saved') } else { //Show snackbar this.showSnackbar('Enter some text first') } } showSnackbar(msg: string) { //Show snackbar this.snackBar.open(msg, '', { duration: 2000, }); } }
เพื่อให้ข้อมูลเชิงบริบทแก่ผู้ใช้ เรายังใช้ MatSnackBar
จากไลบรารีการออกแบบวัสดุ สิ่งนี้จะแสดงเป็นป๊อปอัปจากด้านล่างของหน้าจอและคงอยู่สองสามวินาทีก่อนที่จะหายไป นี่ไม่ใช่องค์ประกอบ แต่เป็นบริการที่สามารถเรียกใช้จากรหัส typescript
ข้อมูลโค้ดด้านบนที่มีชื่อเมธอด showSnackbar
แสดงวิธีการเปิดสแน็คบาร์ แต่ก่อนจะสามารถใช้งานได้ เราจำเป็นต้องเพิ่มรายการ MatSnackBar
ในไฟล์ app.module.ts เช่นเดียวกับที่เราทำกับองค์ประกอบไลบรารีวัสดุอื่นๆ
เคล็ดลับ : ในเวอร์ชันไลบรารีวัสดุของ Angular เวอร์ชันล่าสุด ไม่มีวิธีที่ตรงไปตรงมาในการเปลี่ยนสไตล์สแน็คบาร์ เราต้องเพิ่มรหัสสองรายการแทน
ขั้นแรก ใช้ CSS ด้านล่างเพื่อเปลี่ยนสีพื้นหลังและพื้นหน้า:
::ng-deep snack-bar-container.snackbarColor { background-color: rgba(63, 81, 181, 1); } ::ng-deep .snackbarColor .mat-simple-snackbar { color: white; }
ประการที่สอง ใช้คุณสมบัติที่เรียกว่า panelClass
เพื่อตั้งค่าสไตล์เป็นคลาส CSS ด้านบน:
this.snackBar.open(msg, '', { duration: 2000, panelClass: ['snackbarColor'] });
ชุดค่าผสมสองชุดข้างต้นจะช่วยให้สามารถกำหนดสไตล์เองให้กับคอมโพเนนต์สแน็คบาร์ของไลบรารีดีไซน์ Material
ขั้นตอนในการสร้างหน้า QR เสร็จสมบูรณ์ แต่ยังขาดอยู่ชิ้นหนึ่ง ตรวจสอบไฟล์ create-qr.component.ts จะแสดงข้อผิดพลาดเกี่ยวกับชิ้นส่วนที่หายไป ชิ้นส่วนที่หายไปของปริศนานี้คือ RestutilService
ซึ่งรับผิดชอบในการดึงภาพโค้ด QR จาก API บุคคลที่สาม
ในเทอร์มินัล เปลี่ยนไดเร็กทอรีปัจจุบันเป็นบริการโดยพิมพ์ ng gs restutil
แล้วกด Enter สิ่งนี้จะสร้างไฟล์ RestUtilService เปิดไฟล์ restutil.service.ts และเพิ่มข้อมูลโค้ดนี้:
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
บริการด้านบนดึงภาพ QR จาก API บุคคลที่สาม และเนื่องจากการตอบสนองไม่ใช่ประเภท JSON แต่เป็นรูปภาพ เราจึงระบุประเภทการ responseType
เป็น 'blob'
ในตัวอย่างด้านบน
Angular จัดเตรียมคลาส HttpClient
เพื่อสื่อสารกับเซิร์ฟเวอร์ที่รองรับ HTTP โดยมีคุณสมบัติมากมาย เช่น การกรองคำขอก่อนที่จะเริ่มทำงาน การตอบกลับ ทำให้สามารถประมวลผลการตอบกลับผ่านการเรียกกลับและอื่นๆ หากต้องการใช้เหมือนกัน ให้เพิ่มรายการสำหรับ HttpClientModule ในไฟล์ app.module.ts
สุดท้าย นำเข้าบริการนี้ลงในไฟล์ create-qr.component.ts เพื่อสร้างโค้ด QR ให้เสร็จสิ้น
แต่เดี๋ยวก่อน! มีปัญหากับการสร้างตรรกะ QR ข้างต้น หากผู้ใช้ใช้ข้อความเดิมเพื่อสร้าง QR ซ้ำแล้วซ้ำอีก จะส่งผลให้มีการโทรในเครือข่าย วิธีหนึ่งในการแก้ไขสิ่งนี้คือการแคชคำขอตาม ดังนั้นจึงให้บริการการตอบกลับจากแคชหากข้อความคำขอเหมือนกัน
คำขอแคช
Angular ให้วิธีการที่ง่ายกว่าในการเรียก HTTP, HttpClient พร้อมด้วย HttpInterceptors เพื่อตรวจสอบและแปลงคำขอ HTTP หรือการตอบสนองไปยังและจากเซิร์ฟเวอร์ สามารถใช้สำหรับการรับรองความถูกต้องหรือแคช และหลายสิ่งหลายอย่างดังกล่าว สามารถเพิ่มและเชื่อมโยงตัวดักจับหลายตัวเพื่อการประมวลผลต่อไป ในกรณีนี้ เรากำลังสกัดกั้นคำขอและให้บริการการตอบกลับจากแคชหากข้อความ QR เหมือนกัน
สร้างโฟลเดอร์ interceptor จากนั้นสร้างไฟล์ cache-interceptor.ts :
เพิ่มข้อมูลโค้ดด้านล่างลงในไฟล์:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
ในข้อมูลโค้ดด้านบนนี้ เรามีแผนที่ที่มีคีย์เป็น URL คำขอ และการตอบสนองเป็นค่า เราตรวจสอบว่า URL ปัจจุบันมีอยู่ในแผนที่หรือไม่ if it is, then return the response (the rest is handled automatically). If the URL is not in the map, we add it.
We are not done yet. An entry to the app.module.ts is required for its proper functioning. Add the below snippet:
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CacheInterceptor } from './interceptor/cache-interceptor'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ],
This adds the caching feature to our application. Let's move on to the third page, the History page.
Adding The History Page
All the saved QR codes will be visible here. To create another component, open terminal type ng gc history
and press Enter.
Open history.component.css and add the below code:
.main-content { padding: 5% 10%; } .truncate { width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .center-img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; }
Open history.component.html and replace the content with this:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <div class="main-content"> <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0"> <mat-grid-tile *ngFor="let qr of historyList"> <mat-card> <img mat-card-image src="{{qr.imageBase64}}"> <mat-card-content> <div class="truncate"> {{qr.text}} </div> </mat-card-content> <mat-card-actions> <button mat-button (click)="share(qr.text)">SHARE</button> <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button> </mat-card-actions> </mat-card> </mat-grid-tile> </mat-grid-list> <div class="center-img" *ngIf="historyList.length == 0"> <img src="../../assets/no-see.png" width="256" height="256"> <span>Nothing to see here</span> </div> </div>
As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card
. For the grid view, we are using mat-grid-list
from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.
รายการตาราง Mat ทำหน้าที่เป็นคอนเทนเนอร์ที่มีรายการย่อยหลายรายการที่เรียกว่า mat-grid-tile
ในข้อมูลโค้ด HTML ข้างต้น แต่ละไทล์จะถูกสร้างขึ้นโดยใช้ mat-card
โดยใช้คุณสมบัติบางอย่างสำหรับการจัดวางองค์ประกอบ UI อื่นๆ ทั่วไป เราสามารถระบุ number of columns
และ rowHeight
ซึ่งใช้ในการคำนวณความกว้างโดยอัตโนมัติ ในตัวอย่างข้างต้น เราจะให้ทั้งจำนวนคอลัมน์และค่า rowHeight
เรากำลังใช้ภาพตัวยึดตำแหน่งเมื่อประวัติว่างเปล่า ดาวน์โหลดและเพิ่มลงในโฟลเดอร์ทรัพย์สิน
ในการใช้ตรรกะสำหรับการเติมข้อมูลทั้งหมดนี้ ให้เปิดไฟล์ history.component.ts และเพิ่มตัวอย่างด้านล่างในคลาส HistoryComponent
:
showBackButton = true; title = 'History'; showHistoryNav = false; historyList; constructor(private storageService: StorageutilService, private snackbar: MatSnackBar ) { } ngOnInit() { this.populateHistory(); } private populateHistory() { this.historyList = this.storageService.readAllHistory(); } delete(text: string) { this.storageService.deleteHistory(text); this.populateHistory(); } share(text: string) { this.snackbar.open(text, '', {duration: 2000,}) }
ตรรกะข้างต้นเพียงดึง QR ที่บันทึกไว้ทั้งหมดและเติมหน้าด้วย ผู้ใช้สามารถลบ QR ที่บันทึกไว้ซึ่งจะลบรายการออกจากที่จัดเก็บในเครื่อง
นี่จึงเป็นการปิดองค์ประกอบประวัติของเรา...หรือไม่? เรายังคงต้องเพิ่มการแมปเส้นทางสำหรับส่วนประกอบนี้ เปิด app-routing.module.ts และเพิ่มการแมปสำหรับหน้าประวัติด้วย:
{ path: 'history', component: HistoryComponent },
อาร์เรย์เส้นทางทั้งหมดควรมีลักษณะดังนี้:
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'create', component: CreateQrComponent }, { path: 'history', component: HistoryComponent }, ];
ตอนนี้เป็นเวลาที่ดีที่จะเรียกใช้แอปพลิเคชันเพื่อตรวจสอบโฟลว์ทั้งหมด ดังนั้นให้เปิดเทอร์มินัลแล้วพิมพ์ ng serve
แล้วกด Enter จากนั้นไปที่ localhost:4200
เพื่อตรวจสอบการทำงานของแอพพลิเคชั่น
เพิ่มใน GitHub
ก่อนที่จะดำเนินการตามขั้นตอนการปรับใช้ จะเป็นการดีที่จะเพิ่มโปรเจ็กต์ไปยังที่เก็บ GitHub
- เปิด GitHub
- สร้างที่เก็บใหม่
- ใน VS Code ให้ใช้เทอร์มินัลและทำตามชุดคำสั่งแรกที่กล่าวถึงในคู่มือเริ่มต้นฉบับย่อเพื่อพุชไฟล์โปรเจ็กต์ทั้งหมด
เพียงรีเฟรชหน้าเพื่อตรวจสอบว่าไฟล์ทั้งหมดสามารถมองเห็นได้หรือไม่ จากจุดนี้ไป การเปลี่ยนแปลงของ git (เช่น commit, pull/push) จะปรากฏในที่เก็บที่สร้างขึ้นใหม่นี้
Netlify และการปรับใช้
แอปพลิเคชันของเราทำงานในเครื่องท้องถิ่นของเรา แต่เพื่อให้ผู้อื่นสามารถเข้าถึงได้ เราควรปรับใช้บนแพลตฟอร์มคลาวด์และลงทะเบียนกับชื่อโดเมน นี่คือที่มาของ Netlify ให้บริการการปรับใช้อย่างต่อเนื่อง การผสานรวมกับ GitHub และคุณสมบัติอื่น ๆ อีกมากมายที่จะได้รับประโยชน์ ตอนนี้ เราต้องการเปิดใช้งานการเข้าถึงแอปพลิเคชันของเราทั่วโลก มาเริ่มกันเลย.
- สมัครสมาชิกกับ Netlify
- จากแดชบอร์ด ให้คลิกที่ปุ่ม New site from Git
- คลิกที่ GitHub ในหน้าจอถัดไป
- อนุญาตให้ Netlify เข้าถึงที่เก็บ GitHub ของคุณ
- ค้นหาและเลือกที่เก็บ
qr
ที่สร้างขึ้นใหม่ - ในขั้นตอนต่อไป Netlify ทำให้เราสามารถเลือกสาขาที่เก็บ GitHub สำหรับการปรับใช้ได้ โดยปกติหนึ่งใช้
master
branch แต่หนึ่งสามารถมีrelease
branch แยกต่างหากซึ่งมีเฉพาะคุณสมบัติที่เกี่ยวข้องและเสถียรเท่านั้น
เนื่องจากนี่คือเว็บแอปพลิเคชันเชิงมุม ให้เพิ่ม ng build --prod
เป็นคำสั่ง build ไดเร็กทอรีที่เผยแพร่จะเป็น dist/qr
ตามที่กล่าวไว้ในไฟล์ angular.json
ตอนนี้คลิกที่ปุ่ม Deploy site
ซึ่งจะทริกเกอร์การสร้างโปรเจ็กต์ด้วยคำสั่ง ng build --prod
และจะส่งออกไฟล์ไปยัง dist/qr
เนื่องจากเราให้ข้อมูลพาธแก่ Netlify โปรแกรมจะรับไฟล์ที่ถูกต้องโดยอัตโนมัติเพื่อให้บริการเว็บแอปพลิเคชัน Netlify เพิ่มโดเมนสุ่มให้กับแอปพลิเคชันของเราโดยค่าเริ่มต้น
ตอนนี้คุณสามารถคลิกลิงก์ที่ให้ไว้ในหน้าด้านบนเพื่อเข้าถึงแอปพลิเคชันได้จากทุกที่ ในที่สุด แอปพลิเคชันได้รับการปรับใช้แล้ว
โดเมนที่กำหนดเอง
ในภาพด้านบน URL สำหรับแอปพลิเคชันของเราจะแสดงในขณะที่สร้างโดเมนย่อยแบบสุ่ม มาเปลี่ยนกันเถอะ
คลิกที่ปุ่ม Domain settings
จากนั้นในส่วนโดเมนที่กำหนดเอง ให้คลิกที่เมนู 3 จุด และเลือก Edit site name
ซึ่งจะเปิดป๊อปอัปที่สามารถป้อนชื่อไซต์ใหม่ได้ ชื่อนี้ควรไม่ซ้ำกันในโดเมน Netlify ป้อนชื่อไซต์ที่มีอยู่แล้วคลิก บันทึก
ตอนนี้ลิงก์ไปยังแอปพลิเคชันของเราจะได้รับการอัปเดตด้วยชื่อไซต์ใหม่
การทดสอบแบบแยกส่วน
คุณสมบัติเจ๋ง ๆ อีกประการหนึ่งที่ Netlify นำเสนอคือการทดสอบแบบแยกส่วน ช่วยให้สามารถแยกการรับส่งข้อมูลเพื่อให้ผู้ใช้กลุ่มต่างๆ สามารถโต้ตอบกับการปรับใช้แอปพลิเคชันที่แตกต่างกัน เราสามารถเพิ่มคุณสมบัติใหม่ให้กับสาขาที่แตกต่างกันและแยกการรับส่งข้อมูลไปยังการปรับใช้สาขานี้ วิเคราะห์การรับส่งข้อมูลแล้วรวมสาขาคุณสมบัติกับสาขาการปรับใช้หลัก มากำหนดค่ากัน
ข้อกำหนดเบื้องต้นในการเปิดใช้งานการทดสอบแยกคือที่เก็บ GitHub ที่มีสาขาอย่างน้อยสองสาขา ตรงไปที่ที่เก็บแอปใน GitHub ที่สร้างไว้ก่อนหน้านี้ และสร้างสาขาใหม่ a
.
ที่เก็บตอนนี้จะมีสาขา master
และ a
จำเป็นต้องกำหนดค่า Netlify เพื่อทำการปรับใช้สาขา ดังนั้นให้เปิดแดชบอร์ด Netlify และคลิกที่ Settings
ทางด้านซ้าย ให้คลิกที่ Build & Deploy
จากนั้นคลิก Continuous Deployment
จากนั้นทางด้านขวาในส่วน Deploy contexts
การทำให้ใช้งานได้ ให้คลิกที่ Edit settings
ในส่วนย่อยการ Branch deploys
ให้เลือกตัวเลือก "ให้ฉันเพิ่มสาขาแต่ละสาขา" แล้วป้อนชื่อสาขาและบันทึก
การปรับใช้วงเล็บปีกกาเป็นคุณลักษณะที่มีประโยชน์อีกอย่างหนึ่งที่ Netlify มีให้; เราสามารถเลือกสาขาที่เก็บ GitHub ที่จะปรับใช้ และเรายังสามารถเปิดใช้งานการแสดงตัวอย่างสำหรับคำขอดึงทุกรายการไปยังสาขา master
ก่อนที่จะรวม นี่เป็นคุณสมบัติที่ดีที่ช่วยให้นักพัฒนาสามารถทดสอบการเปลี่ยนแปลงได้จริงก่อนที่จะเพิ่มการเปลี่ยนแปลงโค้ดไปยังสาขาการปรับใช้หลัก
ตอนนี้ คลิกที่ตัวเลือกแท็บ Split Testing
ที่ด้านบนของหน้า การกำหนดค่าการทดสอบแยกจะแสดงที่นี่
เราสามารถเลือกสาขา (นอกเหนือจากสาขาการผลิต) — ในกรณีนี้ a
. เรายังสามารถลองเล่นกับการตั้งค่าการแยกทราฟฟิก ขึ้นอยู่กับเปอร์เซ็นต์การรับส่งข้อมูลแต่ละสาขาที่ได้รับการจัดสรร Netlify จะกำหนดเส้นทางผู้ใช้บางรายไปยังแอปพลิเคชันที่ปรับใช้โดย a
สาขา และคนอื่นๆ ไปยังสาขา master
หลังจากกำหนดค่าแล้ว ให้คลิกปุ่ม Start test
เพื่อเปิดใช้งานการแยกการรับส่งข้อมูล
เคล็ดลับ : Netlify อาจไม่รู้จักว่าที่เก็บ GitHub ที่เชื่อมต่อมีมากกว่าหนึ่งสาขา และอาจแสดงข้อผิดพลาดนี้:
ในการแก้ไขปัญหานี้ เพียงเชื่อมต่อกับที่เก็บอีกครั้งจากตัวเลือก Build & Deploy
Netlify มีคุณสมบัติอื่น ๆ มากมายเช่นกัน เราเพิ่งผ่านฟีเจอร์ที่มีประโยชน์บางอย่างเพื่อแสดงให้เห็นถึงความง่ายในการกำหนดค่าด้านต่างๆ ของ Netlify
สิ่งนี้นำเราไปสู่จุดสิ้นสุดของการเดินทางของเรา เราได้สร้างการออกแบบ Angular Material สำเร็จโดยใช้เว็บแอปพลิเคชันและปรับใช้บน Netlify
บทสรุป
Angular เป็นเฟรมเวิร์กที่ยอดเยี่ยมและเป็นที่นิยมสำหรับการพัฒนาเว็บแอปพลิเคชัน ด้วยไลบรารีการออกแบบวัสดุเชิงมุมอย่างเป็นทางการ มันง่ายกว่ามากในการสร้างแอปพลิเคชันที่เป็นไปตามข้อกำหนดการออกแบบวัสดุเพื่อการโต้ตอบที่เป็นธรรมชาติมากกับผู้ใช้ ยิ่งไปกว่านั้น แอปพลิเคชันที่พัฒนาด้วยเฟรมเวิร์กที่ยอดเยี่ยมควรใช้แพลตฟอร์มที่ยอดเยี่ยมสำหรับการปรับใช้ และ Netlify ก็เป็นเช่นนั้น ด้วยวิวัฒนาการอย่างต่อเนื่อง การสนับสนุนที่ยอดเยี่ยม และคุณสมบัติมากมาย จึงเป็นแพลตฟอร์มที่ยอดเยี่ยมในการนำเว็บแอปพลิเคชันหรือไซต์แบบคงที่มาสู่มวลชน หวังว่าบทความนี้จะให้ความช่วยเหลือในการเริ่มต้นกับโปรเจ็กต์ Angular ใหม่ตั้งแต่ความคิดจนถึงการปรับใช้งาน
อ่านเพิ่มเติม
- สถาปัตยกรรมเชิงมุม
- ส่วนประกอบวัสดุเชิงมุมเพิ่มเติม
- ข้อมูลเพิ่มเติมเกี่ยวกับคุณสมบัติของ Netlify