Xử lý danh sách với v-for Phần 2

Trong phần 1 bạn đã được học các cú pháp cơ bản của cấu trúc vòng lặp v-for, phần tiếp theo này chúng ta cùng nhau xem xét một số phần liên quan đến xử lý danh sách như quản lý khi dữ liệu danh sách thay đổi, thực hiện lọc danh sách, thực hiện sắp xếp danh sách…

1. Kiểm soát thay đổi nguồn dữ liệu v-for

1.1 Các phương thức có thể kiểm soát

Vue.js là framework hoạt động theo mô hình MVVM, chính vì vậy gần như mọi thay đổi dữ liệu ở phía Model sẽ được phản ánh lên View ngay lập tức. Dữ liệu của mảng trong các ví dụ về cú pháp lặp v-for cũng vậy, khi thay đổi nó cũng ngay lập tức tự động cập nhật lên phần nội dung các thẻ HTML. Vuejs có thể kiểm soát được các phương thức dẫn đến thay đổi nội dung của mảng và nó cũng kích hoạt ngay việc cập nhật trên View. Danh sách các phương thức này là:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

Chúng ta tìm hiểu đến phần quản lý sự kiện và thuộc tính methods do vậy chưa thể thiết kế một form để thêm, sửa, xóa các phần tử trong mảng. Tuy nhiên chúng ta cũng có thể tác động đến dữ liệu trong Model thông qua cửa sổ console của trình duyệt hoặc cũng có thể sử dụng Vue DevTools.
Sử dụng console thay đổi dữ liệu mảngMở tab Console bằng phím tắt Ctrl + Shift + I sau đó chọn tab Console. Dòng lệnh đầu:

console.log(vue.users)

để in ra danh sách các người dùng kể cả những người dùng có thuộc tính active = 0. Danh sách này hiện có 7 phần tử, tiếp theo thực hiện lệnh thêm một đối tượng vào mảng đối tượng người dùng.

vue.users.push({name: 'Đoàn Mạnh Toàn', email: 'toandm@allaravel.com', phone: '0904123456', location: 'Hà Nội',active: 1})

Ngay lập tức bạn thấy danh sách người dùng được cập nhật. Ở đây vue chính là biến chứa đối tượng Vue, nó có mảng users. Bạn có thể kiểm tra các phương thức khác trong danh sách các phương thức có thể thay đổi nội dung của mảng ở trên.

1.2 Các phương thức không thể kiểm soát

Các phương thức được liệt kê ở trên khi thực hiện nó thay đổi mảng gốc, có một số phương thức khác sẽ được liệt kê ở dưới đây không thay đổi dữ liệu mảng gốc mà nó trả về một mảng mới.

  • filter()
  • concat()
  • slice()

Do đó khi sử dụng các phương thức này bạn cần gán mảng gốc bằng mảng mới được trả về này. Chúng ta cùng kiểm tra điều này trên màn hình Console của trình duyệt nhé.

Gán lại mảng khi sử dụng phương thức không kiểm soát

vue.users = vue.users.filter(function(item) {return item.name.match('Trần')})

Ở đây chúng ta lọc ra danh sách những người có họ Trần, ở câu lệnh đầu không gán lại mảng gốc nên chúng ta không thấy view thay đổi, tuy nhiên khi gán lại mảng gốc, dữ liệu trên view đã thay đổi theo.

1.3 Một số cách gán giá trị không thể kiểm soát

Do hạn chế của Javascript với một số cách thức gán giá trị khiến Vue.js không thể kiểm soát được:

  • Khi gán giá trị cho một phần tử có index cụ thể: vue.users[6] = {name: ‘Trần Văn Tèo’, …}
  • Khi thay đổi độ dài của mảng: vue.users.length = 10

Không kiểm soát được một số cách gán giá trị

Vậy chúng ta gán giá trị cho các phần tử trong mảng như thế nào? Bạn đừng lo, Vue đã thiết kế cho bạn một số phương thức để thực hiện việc đó:

  • Phương thức toàn cục set: Vue.set(vue.users, 6, {name: ‘Trần Văn Tèo’, …}), chú ý phương thức này có thể dùng theo kiểu khác như sau: vue.$set(vue.users, 6, {name: ‘Trần Văn Tèo’, …}), $set là alias của Vue.set.
  • Sử dụng phương thức splice() của Javascript: vue.urser.splice(6, 1, {name: ‘Trần Văn Tèo’, …})

Hạn chế thứ hai về việc gán độ dài mảng, chúng ta cũng hoàn toàn có thể xử lý phương thức splice() của Javascript như sau: vue.users.splice(10).

Do hạn chế Javascript, Vuejs không thể kiểm soát được việc thêm hoặc xóa các thuộc tính của một đối tượng. Ví dụ mảng đối tượng ở trên, mỗi đối tượng có sẵn các thuộc tính như name, email, phone… chúng ta muốn thêm một thuộc tính ngày gia nhập hệ thống joinedDate lúc runtime như sau:

vue.users[6].joinedDate = '01/01/1990'

Vue.js sẽ không kiểm soát được kiểu gán giá trị như vậy, do vậy bạn cần phải sử dụng phương thức toàn cục set ở trên như sau:

Vue.set(vue.users[3], 'joinedDate', '01/01/1990')

2. Lọc và sắp xếp kết quả khi xử lý danh sách bằng v-for

Trong phiên bản Vuejs 1.0 khi duyệt qua các phần tử trong danh sách chúng ta có thể lọc và sắp xếp với tùy chọn filterBy, sortBy ví dụ lọc và hiển thị tất cả những người họ Trần:

<tr v-for="(user, index) in users | filterBy 'Trần' in 'name'" v-if="user.active">
    <th scope="row">{{ index }}</th>
    <td>{{ user.name }}</td>
    <td>{{ user.email }}</td>
    <td>{{ user.phone }}</td>
    <td>{{ user.location }}</td>
</tr>

Từ phiên bản Vue.js 2.x trở đi các tùy chọn này được bỏ đi trong sự tiếc nuối rất nhiều người dùng, bạn có thể xem issue này trên Github để thấy được sự tranh luận nảy nửa khi bỏ tùy chọn này, lý do như sau:

  • Vue.js sẽ tập trung vào nhiệm vụ là một framework hơn là đi viết lại các chức năng mà một số thư hiện đã có sẵn như axios, moment, lodash…
  • Bạn có thể sử dụng thuộc tính computed trong Vue để thực hiện việc lọc mảng, các giá trị ở đây có thể sử dụng lại chứ không như filterBy bạn đưa vào v-for chỉ phục vụ riêng cho v-for đó.

Như vậy từ phiên bản Vue.js 2.0 trở đi chúng ta sẽ thực hiện việc lọc danh sách trong thuộc tính computed hoặc qua các phương thức trong methods. Kiến thức này sẽ được giới thiệu đến bạn trong những bài kế tiếp của Khóa học Vue.js miễn phí, trong khuôn khổ bài viết này chúng ta cũng vẫn làm một ví dụ nho nhỏ, nếu bạn chưa hiểu hết các đoạn mã trong này thì cũng đừng quá lo nhé.

Bộ lọc trong xử lý mảng

<!DOCTYPE html>
<html>
<head>
    <title>Xử lý lọc dữ liệu trong Vuejs - Allaravel.com</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
    <div id="app" class="container">        
        <div class="row">
            <div class="col-md-12 text-center">
                <input type="text" v-model="searchString" class="form-control" placeholder="Nhập từ khóa tìm kiếm">
            </div>
        </div>
        <div class="row">
            <div class="col-md-12" v-for="post in filteredPosts">
                <a :href="post.url">{{ post.title }}</a>
            </div>
        </div>
    </div>
    <script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                searchString: "",
                posts: [
                    {title: 'Học Laravel 5 miễn phí', url: 'https://allaravel.com/laravel-tutorials/khoa-hoc-laravel-5-mien-phi/'},
                    {title: 'Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel', url: 'https://allaravel.com/laravel-tutorials/tim-kiem-thong-minh-voi-typeahead-trong-ung-dung-laravel/'},
                    {title: 'Xác thực API bằng OAuth2 với Laravel Passport', url: 'https://allaravel.com/laravel-tutorials/xac-thuc-api-bang-oauth-2-voi-laravel-passport/'},
                    {title: 'Khóa học Vue.js miễn phí', url: 'https://allaravel.com/tutorials/vuejs-framework/khoa-hoc-vue-js-2-mien-phi/'},
                    {title: 'Series Xây dựng diễn đàn dạng đơn trang với Vuejs', url: 'https://allaravel.com/laravel-tutorials/xay-dung-forum-dang-spa-voi-laravel-va-vue-js/'},
                    {title: 'Vue.js là gì?', url: 'https://allaravel.com/tutorials/vuejs-framework/gioi-thieu-framework-vue-js/'},
                    {title: 'Inversion of Control nguyên lý của các nguyên lý', url: 'https://allaravel.com/tutorials/inversion-of-control-nguyen-ly-cua-cac-nguyen-ly/'},
                    {title: 'Design Pattern sự tiến hóa trong lập trình', url: 'https://allaravel.com/tutorials/design-pattern-su-tien-hoa-trong-lap-trinh/'},
                    {title: 'SOLID 5 nguyên lý vàng trong thiết kế hướng đối tượng', url: 'https://allaravel.com/laravel-tutorials/solid-5-nguyen-ly-vang-trong-thiet-ke-huong-doi-tuong/'}
                ]
            },
            computed: {
                filteredPosts: function() {
                    var postArr = this.posts,
                        searchString = this.searchString;
                    if(!searchString){
                        return postArr;
                    }
                    searchString = searchString.trim().toLowerCase();
                    postArr = postArr.filter( function(item) {
                        if(item.title.toLowerCase().indexOf(searchString) !== -1) {
                            return item;
                        }
                    })
                    return postArr;
                }
            }
        })
    </script>
</body>
</html>

3. Bài tập phần 1

Lọc và sắp xếp kết quả cần sử dụng các kiến thức khác, do vậy trong phần 2 của Xử lý danh sách với v-for sẽ không có bài tập, chúng ta cùng xem lại bài tập trong phần 1. Toàn bộ lời giải cho bài tập phần 1 như sau:

<!DOCTYPE html>
<html>
<head>
    <title>Xử lý lặp có điều kiện v-for Vuejs - Allaravel.com</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
    <div id="app" class="container">        
        <div class="row">
            <div class="col-md-12 text-center">
                <h1 class="text-success">Những bài viết được xem nhiều nhất trên All Laravel</h1>
            </div>
        </div>
        <div class="row" v-for="category in mostViewedCategories">
            <div class="col-md-12">
                <h3>{{ category.name }}</h3>
            </div>
            <div class="col-md-12" v-for="(post, index) in category.posts">
                {{ index + 1 }}. <a :href="post.url">{{ post.title }}</a>
            </div>
        </div>
    </div>
    <script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#app',
            data: {
                mostViewedCategories: [
                    {
                        name: 'Kiến thức framework Laravel', 
                        posts: [
                            {title: 'Học Laravel 5 miễn phí', url: 'https://allaravel.com/laravel-tutorials/khoa-hoc-laravel-5-mien-phi/'},
                            {title: 'Tìm kiếm thông minh với Typeahead trong ứng dụng Laravel', url: 'https://allaravel.com/laravel-tutorials/tim-kiem-thong-minh-voi-typeahead-trong-ung-dung-laravel/'},
                            {title: 'Xác thực API bằng OAuth2 với Laravel Passport', url: 'https://allaravel.com/laravel-tutorials/xac-thuc-api-bang-oauth-2-voi-laravel-passport/'}
                        ]
                    },
                    {
                        name: 'Kiến thức framework Vue.js', 
                        posts: [
                            {title: 'Khóa học Vue.js miễn phí', url: 'https://allaravel.com/tutorials/vuejs-framework/khoa-hoc-vue-js-2-mien-phi/'},
                            {title: 'Series Xây dựng diễn đàn dạng đơn trang với Vuejs', url: 'https://allaravel.com/laravel-tutorials/xay-dung-forum-dang-spa-voi-laravel-va-vue-js/'},
                            {title: 'Vue.js là gì?', url: 'https://allaravel.com/tutorials/vuejs-framework/gioi-thieu-framework-vue-js/'}
                        ]
                    },
                    {
                        name: 'Design Pattern', 
                        posts: [
                            {title: 'Inversion of Control nguyên lý của các nguyên lý', url: 'https://allaravel.com/tutorials/inversion-of-control-nguyen-ly-cua-cac-nguyen-ly/'},
                            {title: 'Design Pattern sự tiến hóa trong lập trình', url: 'https://allaravel.com/tutorials/design-pattern-su-tien-hoa-trong-lap-trinh/'},
                            {title: 'SOLID 5 nguyên lý vàng trong thiết kế hướng đối tượng', url: 'https://allaravel.com/laravel-tutorials/solid-5-nguyen-ly-vang-trong-thiet-ke-huong-doi-tuong/'}
                        ]
                    }
                ]
            }
        })
    </script>
</body>
</html>

Kết quả như sau:

Add Comment