Trong bài viết này, chúng ta sẽ sử dụng phiên bản gần đây nhất của Angular (Angular 9) để xây dựng một ứng dụng Progressive Web Application (PWA) đơn giản. Ứng dụng sẽ lấy ra thông tin người dùng và hiển thị dưới dạng bảng, có phân trang.
PWA là gì ?
PWA (viết tắt của Progressive Web Apps) là một thuật ngữ để biểu thị cho phương pháp tiến bộ phát triển phần mềm. PWA sử dụng
khả năng của một website hiện đại để cung cấp 1 trải nghiệm như là 1 ứng dụng gốc đến với người dùng
PWA có nhiều tính năng nổi bật như hỗ trợ ứng dụng web chạy ngoại tuyến và giúp ứng dụng web trông như một ứng dụng gốc bằng cách sử dụng service worker, dễ dàng tiếp cận với người dùng bằng chức năng thêm vào màn hình chính của Web App Manifest, Push Notification…Nhiều framework đã tích hợp với PWA như Angular, React, Polymer, Ember…
Cấu hình và cài đặt Angular
Tại bước này, chúng ta sẽ cấu hình Angular project cho demo của chúng ta.
Đầu tiên, hãy bắt đầu với việc cài đặt bản Angular CLI mới nhất nhé.
1 2 | npm install -g @angular/<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a> |
Giờ thì bắt đầu cài ứng dụng Angular
1 2 | ng new angular-pwa |
Truy cập vào folder chứa ứng dụng vừa tạo
1 2 | cd angular-pwa |
Thêm thư viện Angular Material
Việc thêm thư viện Material vào Angular thực sự rất dễ dàng, bạn có thể hoàn thành nó chỉ bằng một lệnh sau:
1 2 | ng add @angular/material |
Thêm theme trong src/styles.css
1 2 | @import "<a href="/cdn-cgi/l/email-protection" class="__cf_email__">[email protected]</a>/material/prebuilt-themes/indigo-pink.css"; |
Thông thường, chúng ta sẽ import các compoent của angular material trong AppModule. Nhưng ở đây sẽ có một chút thay đổi, chúng ta sẽ tạo một file module riêng cho các component material và import chúng vào đó. Sau đó import file này vào file AppModule chính.
Việc này sẽ giúp việc quản lý các material component trở nên chặt chẽ, có tổ chức hơn. Chúng ta sẽ hiển thị dữ liệu người dùng vào bảng của angular material.
Tạo file app/material.module.ts
và thêm đoạn code sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">import</span> <span class="token punctuation">{</span> NgModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MatTableModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/material/table'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MatPaginatorModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/material/paginator'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MatToolbarModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/material/toolbar'</span><span class="token punctuation">;</span> @<span class="token function">NgModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span> imports<span class="token punctuation">:</span> <span class="token punctuation">[</span> MatTableModule<span class="token punctuation">,</span> MatPaginatorModule<span class="token punctuation">,</span> MatToolbarModule <span class="token punctuation">]</span><span class="token punctuation">,</span> exports<span class="token punctuation">:</span> <span class="token punctuation">[</span> MatTableModule<span class="token punctuation">,</span> MatPaginatorModule<span class="token punctuation">,</span> MatToolbarModule <span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">MaterialModule</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
Tiếp theo, import MaterialModule
vào file app.module.ts
như dưới đây
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">import</span> <span class="token punctuation">{</span> BrowserModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/platform-browser'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> AppComponent <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./app.component'</span><span class="token punctuation">;</span> <span class="token comment">/* angular material */</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> BrowserAnimationsModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/platform-browser/animations'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MaterialModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./material.module'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> NgModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span> @<span class="token function">NgModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span> declarations<span class="token punctuation">:</span> <span class="token punctuation">[</span> AppComponent <span class="token punctuation">]</span><span class="token punctuation">,</span> imports<span class="token punctuation">:</span> <span class="token punctuation">[</span> BrowserModule<span class="token punctuation">,</span> BrowserAnimationsModule<span class="token punctuation">,</span> MaterialModule <span class="token punctuation">]</span><span class="token punctuation">,</span> providers<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> bootstrap<span class="token punctuation">:</span> <span class="token punctuation">[</span>AppComponent<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AppModule</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
Xây dựng REST API sử dụng HttpClient
Ở bước này, chúng ta sẽ tạo angular service để fetch dữ liệu từ remote server sử dụng REST API
Để sử dụng HTTP request, bạn cần import và đăng ký HttpClientModule service trong app.module.ts file.
1 2 3 4 5 6 7 8 | <span class="token keyword">import</span> <span class="token punctuation">{</span> HttpClientModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/common/http'</span><span class="token punctuation">;</span> @<span class="token function">NgModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span> imports<span class="token punctuation">:</span> <span class="token punctuation">[</span> HttpClientModule <span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Giờ là bước generate service. Với Angular, bạn chỉ cần chạy lệnh ở dưới là service sẽ tự động được tạo:
1 2 | ng g service rest-api |
Tiếp theo, chúng ta sẽ viết logic để lấy dữ liệu người dùng bằng cách sử dụng JSONPlaceholderAPI
Mở file app/rest-api.service.ts
và thêm nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> Observable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> HttpClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/common/http'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">User</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> string<span class="token punctuation">;</span> name<span class="token punctuation">:</span> string<span class="token punctuation">;</span> email<span class="token punctuation">:</span> string<span class="token punctuation">;</span> website<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token punctuation">}</span> @<span class="token function">Injectable</span><span class="token punctuation">(</span><span class="token punctuation">{</span> providedIn<span class="token punctuation">:</span> <span class="token string">'root'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">RestApiService</span> <span class="token punctuation">{</span> api<span class="token punctuation">:</span> string <span class="token operator">=</span> <span class="token string">"https://jsonplaceholder.typicode.com/users"</span><span class="token punctuation">;</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> http<span class="token punctuation">:</span> HttpClient<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token function">getUsers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Observable<span class="token operator"><</span>User<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>http<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token operator"><</span>User<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>api<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Đoạn code trên sẽ giúp chúng ta lấy user data bằng cách sử dụng HttpClient
service như một Observable
từ phương thức getUsers()
.
Tiếp theo, thêm phần service vào file app/app.component.ts
như bên dưới.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <span class="token keyword">import</span> <span class="token punctuation">{</span> Component<span class="token punctuation">,</span> ViewChild <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> RestApiService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./rest-api.service'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MatPaginator <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/material/paginator'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> MatTableDataSource <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/material/table'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">TableElement</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> string<span class="token punctuation">;</span> name<span class="token punctuation">:</span> string<span class="token punctuation">;</span> email<span class="token punctuation">:</span> string<span class="token punctuation">;</span> website<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token punctuation">}</span> @<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token punctuation">:</span> <span class="token string">'app-root'</span><span class="token punctuation">,</span> templateUrl<span class="token punctuation">:</span> <span class="token string">'./app.component.html'</span><span class="token punctuation">,</span> styleUrls<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'./app.component.css'</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AppComponent</span> <span class="token punctuation">{</span> Data<span class="token punctuation">:</span> TableElement<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> col<span class="token punctuation">:</span> string<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'id'</span><span class="token punctuation">,</span> <span class="token string">'name'</span><span class="token punctuation">,</span> <span class="token string">'email'</span><span class="token punctuation">,</span> <span class="token string">'website'</span><span class="token punctuation">]</span><span class="token punctuation">;</span> dataSource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MatTableDataSource</span><span class="token operator"><</span>TableElement<span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>Data<span class="token punctuation">)</span><span class="token punctuation">;</span> @<span class="token function">ViewChild</span><span class="token punctuation">(</span>MatPaginator<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token keyword">static</span><span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> paginator<span class="token punctuation">:</span> MatPaginator<span class="token punctuation">;</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> restApiService<span class="token punctuation">:</span> RestApiService<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>restApiService<span class="token punctuation">.</span><span class="token function">getUsers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>dataSource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MatTableDataSource</span><span class="token operator"><</span>TableElement<span class="token operator">></span><span class="token punctuation">(</span>res<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>dataSource<span class="token punctuation">.</span>paginator <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>paginator<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Vậy là chúng ta đã hoàn thành việc import RestApiService trong AppComponent để fetch dữ liệu user. Giờ là bước hiển thị chúng ta màn hình.
Tại đây, chúng ta sẽ dựng UI của app sử dụng angular material table. Mở file app.component.html
để tạo layout gồm có navbar và bảng với phân trang.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mat-toolbar</span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>accent<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Angular PWA Example<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mat-toolbar</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>table</span> <span class="token attr-name">mat-table</span> <span class="token attr-name">[dataSource]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>dataSource<span class="token punctuation">"</span></span> <span class="token attr-name">matSort</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ng-container</span> <span class="token attr-name">matColumnDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">mat-header-cell</span> <span class="token attr-name">*matHeaderCellDef</span> <span class="token attr-name">mat-sort-header</span><span class="token punctuation">></span></span> ID. <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">mat-cell</span> <span class="token attr-name">*matCellDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>let element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> {{element.id}} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ng-container</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ng-container</span> <span class="token attr-name">matColumnDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">mat-header-cell</span> <span class="token attr-name">*matHeaderCellDef</span> <span class="token attr-name">mat-sort-header</span><span class="token punctuation">></span></span> Name <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">mat-cell</span> <span class="token attr-name">*matCellDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>let element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> {{element.name}} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ng-container</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ng-container</span> <span class="token attr-name">matColumnDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">mat-header-cell</span> <span class="token attr-name">*matHeaderCellDef</span> <span class="token attr-name">mat-sort-header</span><span class="token punctuation">></span></span> Email <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">mat-cell</span> <span class="token attr-name">*matCellDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>let element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> {{element.email}} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ng-container</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ng-container</span> <span class="token attr-name">matColumnDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>website<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">mat-header-cell</span> <span class="token attr-name">*matHeaderCellDef</span> <span class="token attr-name">mat-sort-header</span><span class="token punctuation">></span></span> Website <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">mat-cell</span> <span class="token attr-name">*matCellDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>let element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> {{element.website}} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ng-container</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">mat-header-row</span> <span class="token attr-name">*matHeaderRowDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span> <span class="token attr-name">mat-row</span> <span class="token attr-name">*matRowDef</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>let row; columns: col;<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>table</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>mat-paginator</span> <span class="token attr-name">[pageSizeOptions]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>[7, 14, 28]<span class="token punctuation">"</span></span> <span class="token attr-name">showFirstLastButtons</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>mat-paginator</span><span class="token punctuation">></span></span> |
Thêm PWA vào Angular
Rất dễ dàng để chuyển đổi một ứng dụng angular có sẵn thành Progressive Web App (PWA). Lệnh “ng add angular pwa” sẽ hiện thực hoá việc này
1 2 | ng add @angular/pwa |
Dòng lệnh ở trên sẽ tự động thêm các file PWA và tính năng vào ứng dụng Angular
- File manifest.webmanifest
- ngsw-config.json service worker
- Các icons với đa dạng kích thước trong thư mục assets/icons
File “index.html” sẽ được thêm các thẻ meta và thuộc tính theme color như dưới đây
1 2 3 4 | <link rel="manifest" href="manifest.webmanifest"> <meta name="theme-color" content="#1976d2"> |
Service Workers trong Angular
Service Worker là một tập lệnh hoạt động ở chế độ nền và phù hợp với hầu hết các trình duyệt hiện đại.
Service Worker làm việc với HTTPS và hoạt động tương tự như Web Workers thực hiện nhưng cũng có một số bất lợi. Progressive Web Application coi service workers là công nghệ cốt lõi. Nó cho phép tích hợp nền tảng sâu, như hỗ trợ ngoại tuyến, đồng bộ hóa nền, bộ nhớ đệm phong phú và thông báo (push notifications).
Lệnh “ng add angular pwa” được chạy đã tạo ra tệp ngsw-config.json
. Nó chỉ chịu trách nhiệm service workers.
Đồng thời, service workers cũng đã được tự động thêm vào file app.module.ts
.
// app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">import</span> <span class="token punctuation">{</span> ServiceWorkerModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/service-worker'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> environment <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../environments/environment'</span><span class="token punctuation">;</span> @<span class="token function">NgModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span> declarations<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">,</span> imports<span class="token punctuation">:</span> <span class="token punctuation">[</span> ServiceWorkerModule<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'ngsw-worker.js'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> enabled<span class="token punctuation">:</span> environment<span class="token punctuation">.</span>production <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> providers<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">,</span> bootstrap<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">,</span> schemas<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AppModule</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
Còn đây là file ngsw-config.json.
// ngsw-config.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | { "$schema": "./node_modules/@angular/service-worker/config/schema.json", "index": "/index.html", "assetGroups": [ { "name": "app", "installMode": "prefetch", "resources": { "files": [ "/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js" ] } }, { "name": "assets", "installMode": "lazy", "updateMode": "prefetch", "resources": { "files": [ "/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" ] } } ] } |
Lệnh “ng add angular pwa” cũng tự động đăng ký service-worker vào trong package.json file:
// package.json
1 2 3 4 5 6 7 8 | { "dependencies": { ... "@angular/service-worker": "~8.2.14" ... } } |
Cấu hình Production với http-server
Cài đặt http-server global từ NPM
1 2 | sudo npm install -g http-server |
http-server rất đơn giản, cấu hình dễ dàng, thường dùng để testing, phát triển ở local hoặc mục đích nghiên cứu.
– http-server
Để build ứng dụng cho môi trường production, chạy lệnh sau
1 2 | ng build --prod |
Sau khi chạy lệnh, bản sẽ có bản build trong folder dist/angular-pwa. Tiếp theo, chúng ta sẽ chạy ứng dụng angular PWA sử dụng http-server
Vào trong thư mục prod build
1 2 | cd dist/angular-pwa |
Để chạy lên bản build, thực hiện dòng lệnh sau:
1 2 | http-server -o |
Lệnh trên sẽ mở angular app theo đường dẫn http://127.0.0.1:8080 và lúc này bạn đã một app build hoàn chỉnh rồi đó.
Kiểm tra PWA App với Lighthouse
Chúng ta sẽ verify ứng dụng PWA bằng cách sử dụng Lighthouse extension trên Google Chrome.
Đầu tiên, mở app trên Chrome bằng cách truy cập vào đường dẫn: http://127.0.0.1:8080
Cài đặt light house extension từ chrome web store
Tiếp theo, mở browser console bằng cách sử dụng Ctrl + Shift + I, hoặc mở DevTools, vào tab Audits. Lúc này bạn sẽ thấy kết quả của việc config ứng dụng với PWA
Tổng kết
Cuối cùng, chúng ta cũng đã hoàn thành việc tạo 1 ứng dụng Angular 9 PWA. Tổng kết lại thì bài viết đã nhắc đến một số vấn đề sau:
- Làm thế nào để chuyển đổi ứng dụng angular có sẵn thành PWA?
- Làm thế nào để thêm các tính năng PWA vào ứng dụng Angular?
- Cách hoạt động với Service Workers?
- Cách để kiểm tra PWA app với Google’s Lighthouse extension?
Bạn có thể download bản demo tại đây.
Cảm ơn các bạn đã theo dõi bài viết. Hy vọng nó sẽ giúp ích cho các bạn.
Nguồn: https://www.positronx.io/build-progressive-web-app-pwa-with-angular/