Hướng dẫn lập trình Angular từ đầu từng bước (part 2)
Ở hướng dẫn trước, bạn đã tìm hiểu Angular là gì, so sánh các phiên bản Angular, thiết lập môi trường lập trình Angular và đã tạo ứng dụng Angular đơn giản đầu tiên. Nếu bạn bỏ lỡ hướng dẫn này thì có thể xem ở bài viết sau:
Trong hướng dẫn này, bạn sẽ tìm hiểu kiến trúc ứng dụng Angular, cấu trúc một dự án Angular, định tuyến và điều hướng ứng dụng Angular. Chúng ta bắt đầu thôi!
Từng bước xây dựng ứng dụng Angular CRUD
Hướng dẫn này sẽ hướng dẫn bạn qua các bước tạo một ứng dụng Angular CRUD với TypeScript.
Ứng dụng ví dụ này nhằm mục đích giúp bạn tìm hiểu các khái niệm cơ bản về Angular Framework. Ứng dụng có dạng câu hỏi và câu trả lời (Q&A), người dùng sẽ có thể đặt câu hỏi về các khái niệm chính Angular khác nhau và trả lời những câu hỏi đó từ những người dùng khác. Nó sẽ có danh sách một số khái niệm chính của Angular như Angular CLI và Typescript và trong mỗi danh mục này, một danh sách câu hỏi với các câu trả lời tương ứng, tùy chọn bỏ phiếu cho chúng (phiếu thuận, phiếu chống) và một số form để tạo / xóa / cập nhật câu hỏi và câu trả lời.
Ảnh chụp màn hình sau đây là từ trang chủ, nơi bạn có thể xem danh sách các danh mục:
Kế hoạch cho hướng dẫn này là xây dựng một ứng dụng đưa bạn từng bước từ thiết lập đến một ví dụ đầy đủ tính năng nhằm thể hiện các đặc điểm thiết yếu của một ứng dụng chuyên nghiệp: cấu trúc dự án hợp lý, ràng buộc dữ liệu, dịch vụ, resolver, đường ống, Angular material, dependency injection, điều hướng và truy cập dữ liệu từ xa.
Chúng ta sẽ học đầy đủ về Angular core để bắt đầu và có được sự tự tin rằng Angular có thể làm bất cứ điều gì chúng ta cần. Chúng tôi sẽ trình bày nhiều thông tin cơ bản ở mức độ giới thiệu nhưng chúng tôi cũng sẽ liên kết đến nhiều tài liệu tham khảo đến các chủ đề có chiều sâu hơn.
Ứng dụng bao gồm các thao tác CRUD (Create, Read, Update, Delete) Câu hỏi và Câu trả lời, nơi mọi người có thể đăng câu hỏi mới và trả lời câu hỏi của người khác.
Trong hướng dẫn tiếp theo, chúng ta sẽ khám phá cách tạo backend cho ứng dụng Angular này, sử dụng ngăn xếp MEAN.
Ứng dụng sẽ có các chức năng sau:
- Quản lý câu hỏi (Tạo, Xóa)
- Quản lý câu trả lời (Tạo, Cập nhật, Xóa)
- Liệt kê tất cả các Câu hỏi tạo thành một danh mục ở định dạng danh sách.
- Liệt kê tất cả các Câu trả lời từ một Câu hỏi cụ thể ở định dạng danh sách.
- Cho phép mọi người bỏ phiếu Câu hỏi và Câu trả lời (phiếu thuận, phiếu chống)
Ứng dụng cuối cùng sẽ trông như thế này:
Trong phần đầu tiên này, bạn sẽ học cách:
- Tạo các class để đại diện cho các đối tượng từ model.
- Tạo service để tạo, cập nhật và xóa các đối tượng.
- Tạo các trang và component để đại diện cho các chức năng và hiển thị giao diện người dùng.
Kiến trúc ứng dụng Angular
Angular là một framework được thiết kế để xây dựng các ứng dụng SPA (Single Page Application) và hầu hết thiết kế kiến trúc của nó đều tập trung vào việc thực hiện điều đó một cách hiệu quả.
Ứng dụng SPA là các ứng dụng được truy cập thông qua trình duyệt web giống như các trang web khác nhưng cung cấp nhiều tương tác động hơn giống như các ứng dụng dành cho thiết bị di động và máy tính để bàn. Sự khác biệt đáng chú ý nhất giữa một trang web thông thường và SPA là số lần làm mới trang giảm xuống.
Thông thường, 95% mã SPA chạy trong trình duyệt; phần còn lại hoạt động trong máy chủ khi người dùng cần dữ liệu mới hoặc phải thực hiện các hoạt động bảo mật như xác thực.
Do đó, quá trình kết xuất trang chủ yếu diễn ra ở phía máy khách.
Module trong Angular
Module giúp tổ chức một ứng dụng thành các khối chức năng gắn kết bằng cách gói các component, pipe, directive và service. Chúng chỉ là tất cả về công thái học của nhà phát triển.
Các ứng dụng Angular có dạng module. Mọi ứng dụng Angular đều có ít nhất một module — đó là module gốc, thường được đặt tên là AppModule. Module gốc có thể là module duy nhất trong một ứng dụng nhỏ, nhưng hầu hết các ứng dụng đều có nhiều module hơn. Với tư cách là nhà phát triển, bạn quyết định cách sử dụng các module. Thông thường, bạn ánh xạ chức năng chính hoặc một tính năng cho một module. Giả sử bạn có bốn lĩnh vực chính trong hệ thống của mình. Mỗi cái sẽ có module riêng ngoài module gốc, tổng cộng là năm module.
Bất kỳ module Angular nào đều là một lớp có decorator là @NgModule. Decorator là các hàm sửa đổi các lớp JavaScript. Về cơ bản, chúng được sử dụng để đính kèm siêu dữ liệu vào các lớp để nó biết cấu hình của các lớp đó và cách chúng hoạt động. Các thuộc tính của decorator @NgModule mô tả module là:
- declarations: Các lớp thuộc module này và có liên quan đến các view. Có ba lớp trong Angular có thể chứa view: component, directive và pipe.
- exports: Các lớp mà các component của module khác có thể truy cập được.
- import: Các module có các lớp cần thiết bởi các component của module này.
- providers: Các service ở một trong các module sẽ được sử dụng trong các module hoặc component khác. Khi một service được đưa vào các nhà cung cấp, nó sẽ có thể truy cập được trong tất cả các phần của ứng dụng đó.
- bootstrap: Component gốc là giao diện chính của ứng dụng. Chỉ có module gốc mới có thuộc tính này và nó cho biết component sẽ được khởi chạy.
- entryComponents: Entry component là bất kỳ component nào mà Angular tải theo thứ bậc, (có nghĩa là bạn không tham chiếu nó trong template), theo loại.
Component trong Angular
Component là khối xây dựng cơ bản nhất của giao diện người dùng và các ứng dụng Angular. Một component điều khiển một hoặc nhiều phần trên màn hình (cái mà chúng tôi gọi là view). Trong ví dụ này, chúng ta có các component như AppComponent
(các thành phần bootstrapped), CategoriesComponent
, CategoryQuestionsComponent
, QuestionAnswersComponent
, vv
Một component là độc lập và đại diện cho một phần giao diện người dùng có thể tái sử dụng thường được cấu thành bởi ba điều quan trọng:
- Một đoạn mã html được gọi là view.
- Một lớp đóng gói tất cả dữ liệu có sẵn và các tương tác với view đó thông qua một API gồm các thuộc tính và phương thức do Angular kiến trúc. Đây là nơi chúng tôi xác định logic ứng dụng (nó làm gì để hỗ trợ view)
- Và phần tử html nói trên còn được gọi là selector.
Sử dụng Angular decorator @Component
, chúng tôi cung cấp siêu dữ liệu bổ sung xác định cách component sẽ được xử lý, khởi tạo và sử dụng trong thời gian chạy. Ví dụ: chúng tôi thiết lập html template liên quan đến view, sau đó, chúng tôi thiết lập html selector mà chúng tôi sẽ sử dụng cho component đó, chúng tôi thiết lập stylesheet cho component đó.
Component chuyển dữ liệu đến view bằng cách sử dụng một quá trình được gọi là liên kết dữ liệu (Data Binding). Điều này được thực hiện bằng cách liên kết các phần tử DOM với các thuộc tính của component. Ràng buộc có thể được sử dụng để hiển thị các giá trị thuộc tính cho người dùng, thay đổi style của phần tử, phản hồi sự kiện của người dùng, v.v.
Một component phải thuộc về một NgModule
để component hoặc ứng dụng khác có thể sử dụng được. Để chỉ định rằng một component là thành viên của một NgModule
, bạn nên liệt kê nó trong thuộc tính khai báo của component đó NgModule
.
Một lưu ý nhỏ về tầm quan trọng của các component theo nguyên tắc kiến trúc phần mềm: Điều cực kỳ quan trọng và được khuyến nghị nên có các component riêng biệt, và đây là lý do. Hãy tưởng tượng chúng ta có hai khối giao diện người dùng khác nhau trong cùng một component và trong cùng một tệp. Lúc đầu, chúng có thể nhỏ nhưng mỗi cái có thể phát triển. Chúng tôi chắc chắn sẽ nhận được các yêu cầu mới cho cái này chứ không phải cái khác. Tuy nhiên, mọi thay đổi đều đặt cả hai component vào rủi ro và tăng gấp đôi gánh nặng thử nghiệm mà không có bất kỳ lợi ích nào. Nếu chúng tôi phải sử dụng lại một số khối giao diện người dùng đó ở những nơi khác trong ứng dụng của mình, khối còn lại sẽ được kèm theo.
Kịch bản đó vi phạm Nguyên tắc đơn trách nhiệm (Single Responsibility). Bạn có thể nghĩ rằng đây chỉ là một hướng dẫn, nhưng chúng ta cần phải làm đúng – đặc biệt nếu việc làm đúng là dễ dàng và chúng ta học cách xây dựng ứng dụng Angular trong quá trình này.
Angular khuyến khích nguyên tắc này bằng cách kiểm soát từng bản vá của trang bằng component riêng của nó.
Một ứng dụng Angular điển hình trông giống như một cây các component. Sơ đồ sau đây minh họa khái niệm này. Lưu ý rằng các modal component nằm ở phía bên của component mẹ vì chúng là các component bắt buộc không được khai báo trên component html template.
Khối xây dựng Angular: Template
Template được sử dụng để định nghĩa view component. Template trông giống như HTML thông thường, nhưng nó cũng có một số điểm khác biệt. Mã như * ngFor
, {{hero.name}}
, (click)
và [hero]
sử dụng cú pháp mẫu Angular để nâng cao khả năng của HTML. Template cũng có thể bao gồm các component tùy chỉnh như <custom-element>
ở dạng thẻ html không thông thường. Các component này kết hợp liền mạch với HTML gốc trong cùng một bố cục.
Khối xây dựng Angular: Service
Hầu hết mọi thứ đều có thể là một service, bất kỳ giá trị, function hoặc tính năng nào mà ứng dụng của bạn cần. Service thường là một lớp có mục đích hẹp, được xác định rõ ràng. Nó nên làm một cái gì đó cụ thể và làm nó tốt. Mục đích chính của Angular Services là chia sẻ tài nguyên giữa các component.
Các lớp Component nên tinh gọn, công việc của component là kích hoạt trải nghiệm người dùng (trung gian giữa view và logic ứng dụng) và không có gì hơn. Chúng không tìm nạp dữ liệu từ máy chủ, xác thực đầu vào của người dùng hoặc đăng nhập trực tiếp vào bảng điều khiển. Chúng ủy thác các nhiệm vụ như vậy và mọi thứ không quan trọng cho các service.
Service là nền tảng cho bất kỳ ứng dụng Angular nào và các component là những người dùng lớn của service vì chúng giúp component trở nên tinh gọn.
Kịch bản mà chúng tôi vừa mô tả có liên quan nhiều đến nguyên tắc Tách các Mối quan tâm (Separation of Concerns). Angular không thực thi các nguyên tắc này, nhưng nó giúp bạn tuân theo các nguyên tắc này bằng cách dễ dàng cấu trúc logic ứng dụng của bạn thành các service và cung cấp các service đó cho các component thông qua dependency injection.
Trong ứng dụng của chúng ta có ba service: AnswersService
, QuestionsService
, CategoriesService
. Trong hướng dẫn cụ thể này, chúng ta sẽ chỉ tập trung vào CategoriesService
và trong các phần sau, chúng ta sẽ thảo luận về những phần khác.
CategoriesService
có các phương thức sau:
//gets all the question categories from a local json
getCategories(){
return this.http.get("./assets/categories.json")
.map((res:any) => res.json())
.toPromise();
}
//finds a specific category by slug
getCategoryBySlug(slug: string){
return this.getCategories()
.then(data =>{
return data.categories.find((category) => {
return category.slug == slug;
});
})
}
Khối xây dựng Angular: Các tài nguyên khác
Các tài nguyên bên ngoài như Cơ sở dữ liệu, API, v.v., là cơ bản vì chúng sẽ cho phép ứng dụng của chúng ta tương tác với thế giới bên ngoài.
Còn nhiều điều cần nói về các khối xây dựng cơ bản của các ứng dụng Angular như Dependency Injection, Data Binding, Directives, v.v. Bạn có thể tìm thấy những thứ này và nhiều thông tin khác trong bài đăng sắp tới của chúng tôi về “Lộ trình học Angular”.
Bây giờ, chúng ta hãy đi sâu hơn và ánh xạ cấu trúc dự án với kiến trúc của ứng dụng để chúng ta có thể hiểu rõ hơn cách tất cả các phần tương tác với nhau.
Cấu trúc dự án Angular
Sau khi làm theo hướng dẫn thiết lập để tạo một dự án mới trong phần trước, chúng ta hãy xem qua cấu trúc của ứng dụng Angular của chúng ta. Các thủ tục thiết lập cli cài đặt rất nhiều tệp khác nhau. Hầu hết chúng có thể được bỏ qua một cách an toàn.
Trong thư mục gốc của dự án, chúng ta có ba thư mục quan trọng và một số tệp quan trọng:
- src – Đây là thư mục quan trọng nhất. Ở đây chúng ta có tất cả các tệp tạo nên ứng dụng Angular của chúng ta.
- e2e – Thư mục này dành cho các bài thử nghiệm End-to-end của ứng dụng, được viết bằng Jasmine và chạy bởi người chạy thử nghiệm protractor e2e. Xin lưu ý rằng chúng tôi sẽ không đi vào chi tiết về thử nghiệm trong bài viết này.
- node_modules – Các gói npm được cài đặt trong dự án bằng lệnh npm install.
- package.json – Như mọi ứng dụng web hiện đại, chúng ta cần một hệ thống gói và trình quản lý gói để xử lý tất cả các thư viện và module của bên thứ ba mà ứng dụng của chúng ta sử dụng. Bên trong tệp này, bạn sẽ tìm thấy tất cả các phụ thuộc và một số nội dung hữu ích khác như các tập lệnh npm sẽ giúp chúng ta rất nhiều để sắp xếp quy trình phát triển (gói / biên dịch).
- tsconfig.json – Tệp cấu hình chính. Nó cần phải ở trong thư mục gốc vì đó là nơi mà trình biên dịch typecript sẽ tìm kiếm nó.
Bên trong thư mục /src
chứa mã chưa biên dịch của chúng ta. Đây là nơi mà hầu hết các công việc cho ứng dụng Angular của bạn sẽ diễn ra. Khi chúng ta chạy lệnh ng serve
, mã của chúng ta bên trong thư mục /src
sẽ được đóng gói và chuyển thành phiên bản Javascript chính xác mà trình duyệt hiểu được (hiện tại, ES5). Điều đó có nghĩa là chúng ta có thể làm việc ở cấp độ cao hơn bằng cách sử dụng TypeScript, nhưng biên dịch xuống dạng cũ hơn của Javascript mà trình duyệt cần.
Trong thư mục này, bạn sẽ tìm thấy hai cấu trúc thư mục chính.
- app – có tất cả các component, mdoule, page mà bạn sẽ xây dựng ứng dụng.
- environments – thư mục này để quản lý các biến môi trường khác nhau như dev và prod. Ví dụ, chúng ta có thể có một cơ sở dữ liệu cục bộ cho môi trường phát triển (development) và một cơ sở dữ liệu sản phẩm cho môi trường sản xuất (production). Khi chúng ta chạy, nó sẽ sử dụng theo mặc định là
dev env
. Nếu bạn muốn chạy ở chế độ sản xuất, bạn cần thêm cờ –prod vào lệnhng serve
. - index.html – Đó là trang lưu trữ ứng dụng nhưng bạn sẽ không sửa đổi tệp này thường xuyên, như trong trường hợp của chúng tôi, nó chỉ đóng vai trò là trình giữ chỗ. Tất cả các tập lệnh và kiểu cần thiết để làm cho ứng dụng hoạt động sẽ được đưa vào tự động bởi quy trình gói webpack, vì vậy bạn không phải thực hiện việc này theo cách thủ công. Điều duy nhất hiện lên trong tâm trí tôi, mà bạn có thể đưa vào tệp này, là một số thẻ meta (nhưng bạn cũng có thể xử lý chúng thông qua Angular).
Và có các thư mục phụ nhưng cũng quan trọng khác
- assets – trong thư mục này, bạn sẽ tìm thấy hình ảnh, dữ liệu mẫu json và bất kỳ nội dung nào khác mà bạn có thể yêu cầu trong ứng dụng của mình.
Angular best practice: Thư mục app
Đây là cốt lõi của dự án. Chúng ta hãy xem cấu trúc của thư mục này để bạn có ý tưởng tìm mọi thứ ở đâu và nơi để thêm các module của riêng bạn để điều chỉnh dự án này cho phù hợp với nhu cầu cụ thể của bạn.
Thư mục shared
Các tệp SharedModule
nằm trong thư mục này để chứa các component, directive và pipe dùng chung và chia sẻ chúng với các module cần chúng.
Nó import CommonModule
bởi vì component của nó cần các directive chung. Bạn sẽ nhận thấy rằng nó xuất lại các module khác. Nếu bạn xem lại ứng dụng, bạn có thể nhận thấy rằng nhiều component yêu cầu directive SharedModule
cũng sử dụng NgIf
và NgFor
từ CommonModule
và liên kết với các thuộc tính component với [(ngModel)], một directive trong FormsModule
. Module thực hiện kê khai các thành phần này sẽ phải import CommonModule
, FormsModule
và SharedModule
.
Thư mục styles
Tại đây, bạn sẽ tìm thấy tất cả các biến, mixin, shared styles, v.v., sẽ làm cho ứng dụng của bạn có thể tùy chỉnh và mở rộng.
Có thể bạn chưa biết Sass? Nói một cách ngắn gọn, nó là một tập hợp siêu css sẽ dễ dàng và tăng tốc chu kỳ phát triển của bạn một cách đáng kinh ngạc.
Thư mục services
Tại đây bạn sẽ tìm thấy tất cả các service cần thiết trong ứng dụng này. Mỗi service chỉ có các chức năng liên quan đến nó.
Các thư mục khác
Để đạt được mã dạng module, chúng tôi đã tạo mỗi thư mục cho từng component. Trong các thư mục đó, bạn sẽ tìm thấy mọi tệp liên quan cho các trang được bao gồm trong component đó. Điều này bao gồm html cho bố cục, sass cho style và component trang chính.
app.component.html
Tệp này đóng vai trò là khung của ứng dụng. Thông thường có một <router-outlet> để hiển thị các tuyến đường và nội dung của chúng. Nó cũng có thể được bao bọc bởi nội dung mà bạn muốn có trong mọi trang (ví dụ: thanh công cụ).
app.component.ts
Tệp này là thành phần Angular cung cấp chức năng cho tệp app.component.html mà tôi vừa đề cập.
app-routing.module.ts
Nơi chúng tôi cấu hình định tuyến chính. Các tuyến này được đăng ký với Angular RouterModule trong AppModule. Nếu bạn sử dụng các module tải chậm, các tuyến con của các module tải chậm khác được xác định bên trong các module đó.
app.module.ts
Đây là module chính của dự án sẽ khởi động ứng dụng.
Khi chúng ta tiếp tục hướng dẫn này, chúng ta sẽ tạo nhiều trang hơn và thực hiện điều hướng cơ bản.
Định tuyến và điều hướng ứng dụng Angular
Sau khi xem sơ đồ thành phần và cấu trúc dự án, đây là điều hướng mà chúng tôi đề xuất cho ứng dụng. Chúng tôi bắt đầu trong trang danh mục và từ đó chúng tôi chỉ có thể điều hướng đến trang danh sách các câu hỏi của một trong các danh mục. Sau đó, hãy làm theo các mũi tên từ hình ảnh bên dưới để xem các điều hướng khác có sẵn trong ứng dụng Angular này.
Lưu ý rằng các phương thức tạo, cập nhật và xóa không được hiển thị trong hình ảnh này vì chúng không đại diện cho điều hướng ứng dụng. Một điều quan trọng khác cần xem xét là chúng tôi đã sử dụng Breadcrumbs để điều hướng trở lại các trang trước đó.
Trước khi bắt đầu nghĩ về điều hướng, chúng ta phải xem xét loại và lượng dữ liệu bạn muốn hiển thị trong ứng dụng của mình. Đừng quên rằng bạn sẽ sử dụng điều hướng để hiển thị và cấu trúc dữ liệu của mình, đó là lý do tại sao dữ liệu phải tuân theo cấu trúc thông tin của ứng dụng của bạn chứ không phải ngược lại.
Điều quan trọng là phải duy trì các best practice cho thiết kế điều hướng. Điều này đảm bảo rằng mọi người sẽ có thể sử dụng và tìm thấy các tính năng có giá trị nhất trong ứng dụng của bạn.
Điều hướng tốt, giống như thiết kế tốt, là vô hình.