Chào mừng các bạn trở lại
Bài viết này được dịch từ CSS・Bootstrapによるデザイン - AWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第8回】マニュアル của tác giả k_shimoji, ở phần này chúng ta sẽ bắt đầu làm quen với việc thiết kế giao diện bằng bootstrap.
Bootstrap là gì
Nói ngắn gọn, đây là một CSS framework.
Với việc sử dụng những CSS đã có sẵn ở Bootstrap side, bạn có thể dễ dàng thực hiện một website có các khối design đồng nhất.
- Một khi các thành phần thiết kế trên website của bạn có sự thống nhất, bạn sẽ không cần tự viết CSS nữa mà vẫn có một giao diện đẹp tuyệt vời.
- Còn một điểm nữa là khả năng Responsive, với sự co giãn và hiển thị tốt trên mobile.
Bạn sẽ cảm nhận được rất nhiều thuận lợi khi sử dụng. Bootstrap viết CSS sẵn tuy nhiên không có nghĩa là bạn chỉ có thể dùng css sẵn có của nó, bạn chỉ cần viết thêm CSS theo ý mình cho các CSS đã có trong Bootstrap ở phía dưới là sẽ thay thế rất dễ dàng.
Cách sử dụng
Về cơ bản thì các bạn có thể thiết kế một thành phần trên website của bạn theo chuẩn giao diện bootstrap với việc set chỉ cần thêm class ="className-trong-bootstrap"
vào thẻ HTML là xong.
Bạn cũng có thể hiển thị thanh navigation bar, các hệ thống lưới, cả những popup hay những khung thông báo,... theo phong cách rất đẹp của bootstrap với chỉ một bước đơn giản là thay đổi các class.
Rất khó để nói hết tất cả các đặc điểm, đơn giản thì Bootstrap là hạt nhân có chức năng bố cục, thiết kế, và cả giao diện Responsive.
Mô tả.
Hệ thống lưới
Sẽ có rất nhiều row(hàng)
ở trong container
, và cũng sẽ có rất nhiều col(cột)
trong cấu trúc của mỗi row
.
Tuy nhiên bạn có thể thiết kế chi tiết cho mỗi col
, qua đó quản lý số lượng về chiều ngang để sắp xếp các col
trên một row
.
Format của mỗi col
như sau: col-{size}-{giá trị quản lý số cột}
Với cấu trúc trên, định nghĩa giá trị quản lý số cột là "Nếu có một chiều rộng cụ thể của của đối tượng cha, tổng giá trị sẽ tỉ lệ với số 12 cột, nghĩa là nếu các bạn xét giá trị là 3 thì cột ấy sẽ có chiều rộng là 1/4 của chiều rộng tối đa" qua đó mình sẽ cấu trúc được một cách dễ dàng.
Khá khó để giải thích nhỉ, nhưng không sao, khi thực hành các bạn sẽ dễ hiểu hơn.
Tiếp theo chúng ta sẽ tìm hiểu kích thước chiều rộng và những ký tự đại diện trong bootstrap
size | chữ cái đại diên | Loại màn hình
-------------+--------------+----------
〜767px |xs(xtra small) | Nhỏ nhất
768px〜991px |sm(small) | Nhỏ
992px〜1199px|md(medium) | Bình thường
1200px〜 |lg(large) | Lớn
Ví dụ, bạn đã set 6 cột cho mỗi hàng, nó sẽ giống như dưới đây
<div class="container">
<div class="row">
<div class="col-sm-4 bg-info">success</div>
<div class="col-sm-4 bg-danger">danger</div>
<div class="col-sm-4 bg-info">success</div>
<div class="col-sm-4 bg-danger">danger</div>
<div class="col-sm-4 bg-info">success</div>
<div class="col-sm-4 bg-danger">danger</div>
</div>
</div>
※bg-info
, bg-danger
là class để định nghĩa màu nền. Nó cũng giống những mô tả cho các button với sự xuất hiện của những success
,danger
etc....
Bởi vị cấu trúc cột mình đã set là col-sm-4
,
- Chiều rộng màn hình sẽ là = sm (Lớn hơn tablet một tí)
- Giá trị cột là 4
Như vậy mình đã xác định được cụ thể
- Nếu có một chiều rộng lớn hơn một tablet, bạn muốn hiển thị 3 cột ( vì tỉ lệ 4 với 12 là 1/3).
- Trong trường hợp màn hình bé lại đến một kích thước nhất định, tầm ngang smartphone thì mỗi hàng chỉ hiển thị 1 cột.
... Cũng hơi khó hiểu nhỉ
Thôi chuyển qua phần sau
Các công cụ giúp bạn test bootstrap
Với những công cụ này các bạn có thể test bootstrap mà không cần phải đăng nhập
Bootply - The Bootstrap Playground
Đây là sau khi điền giống ví dụ trên kia, và kết quả sau khi thực hiện.
Chiều rộng 768px hoặc lớn hơn (808px)
Nhở hơn 768px(765px)
Thiết lập nhiều col
Bằng việc thiết lập multiple col, các bạn có thể điều khiển
- Trên smartphone chỉ hiển thị một cột
- 2 cột trên tablet
- 3 cột trên PC
- Với màn hình PC lớn sẽ là 4 cột
Mình cỏ thể dễ dàng kiểm soát được nó.
Bây giờ hãy cùng thực hành với ví dụ sau!
<br><br>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-info">success</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-danger">danger</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-info">success</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-danger">danger</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-info">success</div>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-2 bg-danger">danger</div>
</div>
</div>
Một vài ví dụ bổ sung
Chúng tôi sẽ giới thiệu một vài ví dụ về thiết lập class từ trang chủ của bootstrap.
※ Có rất nhiều ví dụ ở đây, hãy tìm hiểu trên [official website] (http://getbootstrap.com/css/).
Mặc dù tất cả đều là tiếng Anh nhưng tôi nghĩ không có vấn đề gì cả vì tương đối đơn giản
Hình dáng và màu sắc của button
<!-- Standard button -->
<button type="button" class="btn btn-default">Default</button>
<!-- Provides extra visual weight and identifies the primary action in a set of buttons -->
<button type="button" class="btn btn-primary">Primary</button>
<!-- Indicates a successful or positive action -->
<button type="button" class="btn btn-success">Success</button>
<!-- Contextual button for informational alert messages -->
<button type="button" class="btn btn-info">Info</button>
<!-- Indicates caution should be taken with this action -->
<button type="button" class="btn btn-warning">Warning</button>
<!-- Indicates a dangerous or potentially negative action -->
<button type="button" class="btn btn-danger">Danger</button>
<!-- Deemphasize a button by making it look like a link while maintaining button behavior -->
<button type="button" class="btn btn-link">Link</button>
-
btn
để xác định đây là button - `btn-XXXX(primary, success, info, warning, danger) quy định màu sắc
Kết quả thực hiện
テーブル
<table class="table">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>@mdo</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>@fat</td>
</tr>
<tr>
<th scope="row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>@twitter</td>
</tr>
</tbody>
</table>
- Thiết lập mặc định của giao diện với
class=table
Sau đó thì bạn có thể thay đổi một tí hiệu ứng, giao diện nếu bạn thêm giống như ví dụ dưới đây, hãy thử xem!
class | mô tả
------+-----
table-striped|Mỗi hàng cột chẵn lẻ màu sắc sẽ khác nhau
table-bordered|Thêm viền cho khung table
table-hover|Làm nổi bật hàng được rê chuột đến
Nội dung
Chương trình của chúng ta hiện tại,
Mục tiêu của chúng ta là sẽ như thế này
Mục lúc bài học
- Sự chuẩn bị
- Bài 1: Thử nghiệm
- Bài 2: Bắt đầu implement
Chuẩn bị
Phần chuẩn bị sẽ giống nhau ở mỗi bài, vì vậy tôi đã tổng hợp nó ở một entry riêng. Các bạn vui lòng xem ở link này All 12 times of study sessions in do I have use of Git - RESTful application workshops will be built on AWS ~ Web development workshop ~ - Qiita để chuẩn bị .
- Git: Bây giờ mình sẽ tạo branch
vol/08
và làm việc trên nó.
Đầu tiên hãy hoàn thành bước này.
Như bài viết trước đã nói thì ở bài 5 và bài 6 mình đã thay đổi một số trong table database.
- Bài 5
- Tạo Table dành cho việc đăng ký thành viên ( Thực hiện cho chức năng login )
- Bài 6
- Thêm một cột cho table TODO list ( Thêm cột Owner và cột assignee cho chức năng phân công công việc)
Các bạn nên tham khảo lại các link sau để rõ hơn.
Tạo Table dành cho việc đăng ký thành viên
Thêm một cột cho table TODO list
Tải Bootstrap
Bootstrap phiên bản 3 (version3) các bạn có thể tải ở tại đây.
[v4-alpha version] (http://v4-alpha.getbootstrap.com/) cũng đã bắt đầu được tung ra, tuy nhiên thời điểm này tôi sẽ sử dụng phiên bản 3.
Minh sẽ download từ trang chủ.
`https://github.com/twbs/bootstrap/releases/download/v3.3.5/bootstrap-3.3.5-dist.zip các bạn cũng có thể dùng lệnh wget để tải về từ link này.
Sau khi tải file zip, giải nén các file vào /var/www/study/rest-study/app/webroot/
.
Sau khi đăng nhập vào server bằng SSH, chạy các lệnh như dưới đây
`` `bash: download, deployment
cd /var/www/study/rest-study/app/webroot
wget https://github.com/twbs/bootstrap/releases/download/v3.3.5/bootstrap-3.3.5-dist.zip
unzip bootstrap-3.3.5-dist.zip
mv bootstrap-3.3.5-dist bootstrap
rm bootstrap-3.3.5-dist.zip
Khi chạy xong các lệnh thì cây thư mục của chúng ta sẽ như dưới đây.
Vì thư mục gốc này có khá nhiều file chúng ta không sử dụng đến, vì vậy mình có thể tạm xóa đi, tôi đã chú thích ở dưới đây.
※ Xóa những file này sẽ không có vấn đề gì.
```SauKhiChạyLệnh
app/webroot/bootstrap
├── css
│ ├── bootstrap-theme.css #Xóa
│ ├── bootstrap-theme.css.map #Xóa
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.css #Xóa
│ ├── bootstrap.css.map #Xóa
│ └── bootstrap.min.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
└── js
├── bootstrap.js #Xóa
├── bootstrap.min.js
└── npm.js #Xóa
app/webroot/bootstrap
├── css
│ ├── bootstrap-theme.min.css
│ └── bootstrap.min.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.woff2
└── js
└── bootstrap.min.js
-
Tạo branch
vol/08
và làm việc trên branch này. - Sửa các table cho đến thời điểm này
- Tải về bootstrap và các file liên quan
Chuyển sang bài 1 luôn nhé :D
Bài 1: Thử nghiệm
Bootstrap Editor and Playground for JavaScript, CSS, HTML5 and jQuery.
Sẽ có rất nhiều thứ đa dạng cần thử nghiệm, các bạn hãy thử một vài ví dụ liên quan đến bootstrap vào link trên, bất cứ những gì bạn thích.=
Lesson2: Thực hành
Đầu tiên tôi muốn bạn sử dụng 2 file css và javascript của bootstrap
Sau đó đến phần design
Tập hợp CSS, Javascript
CSS sẽ được thêm vào default.ctp, javascript sẽ được thêm vào require-config.
Danh sách file cân chỉnh sửa
Thao tác | file | Mô tả thay đổi |
---|---|---|
Sửa | app/webroot/js/require-config.js | thêm các thiết đặt |
Sửa | app/webroot/js/main.js | Đọc file |
Sửa | app/View/Layouts/default.ctp | Sửa design |
Thêm mới | app/webroot/css/app.css | Các custom CSS |
require-config.js
Thêm các thiết đặt để đọc được javascript của bootstrap.
// cấu hình require
var require = {
// キャッシュ防止
urlArgs: "v=" + (new Date()).getTime(),
// モジュール読み込みのbaseUrlを指定
baseUrl: '/rest-study/js/',
// ファイルのpathを指定
paths : {
'jquery' : 'lib/jquery-2.1.3.min',
'underscore' : 'lib/underscore-min',
'backbone' : 'lib/backbone-min',
'marionette' : 'lib/backbone.marionette.min',
+ 'bootstrap' : '../bootstrap/js/bootstrap.min'
},
// ファイルの依存関係を指定
shim : {
'jquery' : {
exports : '$'
},
'underscore' : {
deps : ['jquery'],
exports : '_'
},
'backbone' : {
deps : ['jquery', 'underscore'],
exports : 'Backbone'
},
'marionette' : {
deps : ['backbone'],
exports : 'Marionette'
},
+ 'bootstrap' : {
+ deps : ['jquery']
+ }
}
};
- Để xác định path của file bootstrap bạn đã tải về, thêm vào
paths
-
baseUrl: '/rest-study/js/'
đây là đường dẫn gốc của các path - Thêm các thiết lập của bootstrap phụ thuộc vào jquery vào
shim
main.js
Bởi vì bootstrap sẽ không load được nếu chỉ set ở require-confis.js, mình cần đọc thêm bootstrap nữa.
//開始
console.log('load main');
require([
'marionette',
+ 'bootstrap'
],
function(){
console.log('run main');
require(['app'], function(Application){
console.log('run main2');
window.application = new Application();
});
});
- Thêm một argument vào hàm require, bạn có thể đọc bằng cách điền tên
bootstrap
mà bạn đã định nghĩa trong require-config.
Tóm tắt các bước thực hiện
-
Sửa theo hướng dẫn trên
app/webroot/js/require-config.js
. -
Sửa theo hướng dẫn trên
app/webroot/js/main.js
. - Xác nhận thay đổi ở giao diện chính ( hiện tại thì vẫn chưa có thay đổi gì. Hãy chắc chắn là các thao tác hành động vẫn không thay đổi)!
Thiết kế lại HTML
File Html cho đến bài viết trước (default.ctp) chỉ được viết tối thiểu "để dành cho việc hiển thị những gì cần hiển thị".
Bây giờ, để sử dụng hệ thống lưới (grid system) như trên, chúng ta cần sửa một chút về cấu trúc của HTML.
Mình sẽ thay đổi class chính, ngoài ra không chỉ bootstrap, mình custom một tí về giao diện theo ý mình nữa.
Dưới đây chỉ là những ví dụ, các bạn có thể tự thiết kế theo ý bạn
※ Sẽ tốt hơn nếu bạn chỉnh sửa sau để thử làm những thứ còn lại.
Đầu tiên, hãy thử một lần chỉ đọc patch của bootstrap.min.css
ở file default.ctp
như ở dưới đây.
Chỉ cần thế này thôi thì thiết kế của site đã bắt đầu thay đổi.
Phần dưới đây mình sẽ sửa để giao diện của các button thay đổi một tí, nhưng hãy thử kiểm tra.
default.ctp
Đây là file chính cần chỉnh sửa.
Khi số lượng chỉnh sửa quá lớn, đưa ra tất cả sau khi chỉnh sửa tôi nghĩ hay hơn chỉ hiển thị diff.
Ngoài ra đây là hiển thị diff để các bạn tham khảo Chương 8 - thực hiện· app/View/Layouts/default.ctp
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- Bootstrap CSS -->
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Application CSS -->
<link href="css/app.css" rel="stylesheet">
<title>TODO List</title>
</head>
<body>
<!-- ヘッダ -->
<div id="header" class="container"></div>
<!-- コンテンツ -->
<div id="main" class="container"></div>
<!-- ヘッダのテンプレート -->
<script type="text/template" id="header-template">
<div>
<div class="row header">
<div class="form-group col-xs-12">
<form class="form-inline pull-right">
<label for="logout"><%- username %>(<%- name %>)</label>
<input type="button" class="btn btn-default btn-sm" id="logout" value="Log out">
</form>
</div>
</div>
</div>
</script>
<!-- TODO一覧表示のレイアウトテンプレート -->
<script type="text/template" id="todo-layout-template">
<div>
<h1>TODOリスト</h1>
<div id="todo-lists"></div>
</div>
</script>
<!-- TODO一覧表示のテンプレート -->
<script type="text/template" id="todo-composite-template">
<div class="row">
<div class="col-xs-12">
<span class="row form-inline">
<div class="input-group col-sm-6 col-xs-12">
<label for="new-todo" class="visible-xs">Todo</label>
<textarea class="form-control todo-item-text" rows="3" id="new-todo" placeholder="Todo?" autofocus></textarea>
</div>
<div class="input-group col-sm-3 col-xs-12">
<label for="user-list" class="visible-xs">Assignee</label>
<select class="form-control" name="assignee" id="user-list"></select>
</div>
<div class="input-group col-sm-3 col-xs-12">
<label class="visible-xs"></label>
<input type="button" id="addTodo" class="btn btn-primary btn-md todo-action-button" value="Add TODO">
</div>
</span>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr class="success">
<th class="col-sm-6" colspan="2">ToDo</th>
<th class="col-sm-2">Owner</th>
<th class="col-sm-2">Assignee</th>
<th class="col-sm-2" colspan="2"></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</script>
<!-- TODO一行分のテンプレート(上のtbody部分に挿入される) -->
<script type="text/template" id="todo-item-template">
<td colspan="2">
<div class="checkbox">
<label class="todo-item-text">
<input type="checkbox" class="toggle" <%- status === '1' ? 'checked' : '' %>><%- todo %>
</label>
</div>
</td>
<td>
<span><%- Owner.name %></span>
</td>
<td>
<span><%- Assignee.name %></span>
</td>
<td class="text-center">
<div class="btn-group">
<a class="btn btn-danger remove-link todo-item-button" href="#">Delete</a>
</div>
</td>
<td>
<div class="btn-group">
<a class="btn btn-success detail-link todo-item-button" href="#todo-lists/<%- id %>">Detail</a>
</div>
</td>
</script>
<!-- 詳細画面のレイアウトテンプレート -->
<script type="text/template" id="todo-detail-layout-template">
<div id="todo-item" class="container"></div>
</script>
<!-- 詳細画面の表示内容テンプレート -->
<script type="text/template" id="todo-detail-item-template">
<div class="row">
<div class="col-xs-12">
<h2>Todo #<%- id %></h2>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div>
<form role="form">
<div class="row">
<div class="form-group col-sm-6">
<label for="edit-todo">Todo</label>
<textarea class="form-control todo-item-text" rows="6" id="edit-todo" autofocus placeholder="Todo?"><%- todo %></textarea>
</div>
</div>
<div class="row">
<div class="form-group col-sm-3">
<label for="user-list">Assignee</label>
<select class="form-control" name="assignee" id="user-list">
</select>
</div>
</div>
<div class="row">
<div class="form-group col-xs-12">
<input type="button" class="btn btn-primary todo-action-button" id="updateTodo" value="Update"></input>
<input type="button" class="btn btn-default todo-action-button" id="updateCancel" value="Cancel"></input>
</div>
</div>
</form>
</div>
</div>
</div>
</script>
<!-- ログイン画面テンプレート -->
<script type="text/template" id="login-layout-template">
<form role="form">
<h2>Login</h2>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-5">
<label for="username">Username</label>
<input class="form-control" type="text" id="username" placeholder="username" autofocus></input>
</div>
</div>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-5">
<label for="password">Password</label>
<input class="form-control" type="password" id="password" placeholder="password"></input>
</div>
</div>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-12">
<input type="button" class="btn btn-success todo-action-button" id="login" value="Login"></input>
</div>
</div>
</form>
<form role="form">
<h2>User Registration</h2>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-5">
<label for="username">Username</label>
<input class="form-control" type="text" id="signup-username" placeholder="username"></input>
</div>
</div>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-5">
<label for="password">Name</label>
<input class="form-control" type="text" id="signup-name" placeholder="name"></input>
</div>
</div>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-5">
<label for="password">Password</label>
<input class="form-control" type="password" id="signup-password" placeholder="password"></input>
</div>
</div>
<div class="row">
<div class="col-sm-1">
</div>
<div class="form-group col-sm-12">
<input type="button" class="btn btn-warning todo-action-button" id="signup" value="Register"></input>
</div>
</div>
</form>
</script>
<!-- require -->
<script type="text/javascript" src="js/require-config.js"></script>
<script type="text/javascript" src="js/lib/require.js" data-main="main.js"></script>
</body>
</html>
- Chú ý
- Load
app.css
(CSS của chính bạn) sau khi đọcbootstrap.min.css
.- Với những CSS đã được định nghĩa trong bootstrap, bạn sẽ có một vài thứ cần viết lại ở
app.css
, vì vậy mình sẽ đọc file này sau. - Ví dụ Kích thước chiều rộng của button mình có thể định nghĩa ở
app.css
theo ý mình.
- Với những CSS đã được định nghĩa trong bootstrap, bạn sẽ có một vài thứ cần viết lại ở
- Load
- Chúng ta sử dụng "grid system" mọi nơi.
- Các bạn có thể copy và paste vào đây để thử nghiệm kết quả ở Bootply.
app.css
/* Lower gripping the inside of inline frames (bootstrap setting overwritten)*/
@media (min-width: 768px) {
.form-inline .input-group {
display: inline-block;
vertical-align: bottom;
}
}
/* Chiều rộng buttons của Todo tren danh sách */
.todo-item-button {
width: 70px;
}
/* Chiều rộng các button khác */
.todo-action-button {
width: 120px;
}
/* Tất cả các đường thẳng ở table, căn giữa theo chiều dọc*/
.table > tbody > tr > td {
word-break: break-all;
vertical-align: middle;
}
/* Cỡ chữ của TODO cho lên large Todo*/
.todo-item-text {
font-size: large;
}
/* ヘッダ部分のレイアウト */
.header {
margin: 5px;
padding: 10px 10px 0px 10px;
background-color: darkslategray;
color: lightcyan;
}
- Đã xong, tất cả đều có giải thích ở comment
Tổng kết
Chúng ta vừa thay đổi cấu trúc HTML để dùng bootstrap, hãy xem lại một lần nữa các bước
-
Chỉnh sửa
app/View/Layouts/default.ctp
theo hướng dẫn trên. -
Chỉnh sửa
app/webroot/css/app.css
theo hướng dẫn trên. - Kiểm tra kết quả.
- Commit lên Git.
Hiển thị dạng diff trên GitHub
Bài 8 - thực hành · suzukishouten-study/rest-study@190c35c
Đó là tất cả chương học này
Lời cảm ơn
Cảm ơn các bạn đã theo dõi bài viết này, nếu có bất kỳ thắc mắc hay đóng góp xây dựng nào các bạn vui lòng comment ở dưới, tôi sẽ rất vui lòng tiếp nhận những góp ý để thay đổi lại cho tốt hơn.