Giới thiệu

Tiếp tục trong series 2018 – Cùng nhau học VueJS, hôm nay mình sẽ tạo một ứng dụng nho nhỏ với Transitions & Animation.
Link github: https://github.com/vanquynguyen/vue-cli-timetable .
Link demo: https://vanquynguyen.github.io/vue-cli-timetable

Ứng dụng có kết hợp giữa VueCLI, event handling, Form Input Bindings và Transitions & Animation.

Nội dung

Khởi tạo VueCLI
Trước khi tạo một VueCLI, đảm bảo rằng bạn đã cài npm hoặc yarn. Mình thì dùng npm =)), bạn nào cài yarn thì vào đây nhé https://yarnpkg.com/en/.
Kiểm tra xem máy của bạn đã cài đặt nodejs và npm chưa

$ nodejs -v
v9.4.0
$ npm -v
5.6.0

Cài đặt vue-cli

$ npm install -g vue-cli

Sau khi cài đặt xong dùng lệnh bên dưới để tạo 1 project vue-cli

$ vue init webpack vue-cli-timetable

Trong file App.vue:

<template>
    <div class="container">
        <div class="row">
            <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
                <h1>Time table ({{tasks.length}})</h1>
                <hr>
                <br><br>
            </div>
        </div>
        <form @submit="addTask">
            <div class="form-group">
                <input v-model="newTask" class="form-control" placeholder="Tôi muốn...">
            </div>
        </form>
        <button class="btn btn-danger" @click="shuffle">Đổi chỗ</button>
        <div v-for="(check, index) in checks" v-bind:key="index">
            <input type="checkbox" v-model="checked" v-bind:value="check" /> 
            {{check}}
        </div>
        <span>Checked: {{ checked }}</span>
        <div class="row">
            <div class="form-group">
                <div class="searchable-container">
                    <transition-group name="list-complete" tag="p">
                        <div class="tasks col-xs-5 col-sm-5 col-md-2 col-lg-2" v-for="(task, index) in filteredTasks" v-bind:key="index">
                            <div class="info-block block-info clearfix">
                                <div class="square-box pull-left">
                                    <span class="glyphicon glyphicon-tags glyphicon-lg"></span>
                                    <span class="glyphicon glyphicon-trash glyphicon-lg" @click="removeTask(index)"></span>
                                </div>
                                <div data-toggle="buttons" class="btn-group bizmoduleselect">
                                    <label class="btn btn-default" id="check" @click="checkBox(index)">
                                        <div class="bizcontent">
                                            <input type="checkbox" autocomplete="off">
                                            <span class="glyphicon glyphicon-ok glyphicon-lg"></span>
                                            <h5>{{task.body}}</h5>
                                        </div>
                                    </label>
                                </div>
                            </div>
                            <br>
                        </div>
                    </transition-group>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data () {
            return {
                tasks: [
                    {body:"Ăn cơm", check:"Unchecked", completed: false},
                    {body:"Tán gái", check:"Unchecked", completed: false},
                    {body:"Lướt face", check:"Unchecked", completed: false},
                    {body:"Học Vue", check:"Unchecked", completed: false},
                    {body:"Ngắm gái", check:"Unchecked", completed: false},
                    {body:"Nhậu", check:"Unchecked", completed: false},
                    {body:"Học Laravel", check:"Unchecked", completed: false},
                    {body:"Ngủ", check:"Unchecked", completed: false},
                    {body:"Play game", check:"Unchecked", completed: false},
                    {body:"Play girl", check:"Unchecked", completed: false},
                ],
                checks: ['Checked', 'Unchecked'],
                newTask: '',
                checked: ,
            }
        },
        computed: {
            filteredTasks(){
                let filterTasks = this.tasks;
                $.each(this.checked, function(value, key){                       
                    filterTasks= filterTasks.filter(function(task){                            
                        return task.check == key;
                    })
                });
                return filterTasks;
            }
        },
        methods: {
        
            checkBox(indexTask) {
                this.tasks[indexTask].check = 'Checked';
            },
            addTask(e){
                e.preventDefault();
                this.tasks.push({
                    body: this.newTask,
                    check: "Unchecked",
                    completed: false
                });
                
                this.newTask = '';
            },
            removeTask(index) {
			    Vue.delete(this.tasks, index);
            },
            
            shuffle() {
                this.tasks = _.shuffle(this.tasks)
            },
        }
    }
</script>

Import file App.vue và style.css vào file main.js

import Vue from 'vue'
import App from './App.vue'
import './style.css';

new Vue({
  el: '#app',
  render: h => h(App)
})

Đừng quên viết css cho ứng dụng, tạo file style.css

ul li {
    list-style-type: none;
}

.slide-fade-enter-active {
    transition: all .3s ease;
}

.slide-fade-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}

.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active below version 2.1.8 */ {
    transform: translateX(10px);
    opacity: 0;
}

.searchable-container{margin:20px 0 0 0}
.searchable-container label.btn-default.active{background-color:#007ba7;color:#FFF}
.searchable-container label.btn-default{width:90%;border:1px solid #efefef;margin:5px; box-shadow:5px 8px 8px 0 #ccc;}
.searchable-container label .bizcontent{width:100%;}
.searchable-container .btn-group{width:90%}
.searchable-container .btn span.glyphicon{
    opacity: 0;
}

.searchable-container .btn.active span.glyphicon {
    opacity: 1;
}

.tasks {
    transition: all 1s;
    display: inline-block;
    margin-right: 10px;
}

.list-complete-enter, .list-complete-leave-to
    /* .list-complete-leave-active below version 2.1.8 */ {
    opacity: 0;
    transform: translateY(30px);
}
    .list-complete-leave-active {
    position: absolute;
}

.row {
    padding-left: 122px;
}

Cuối cùng là file index.html

<!DOCTYPE html>
<html lang="en">
    <head>
    <meta charset="utf-8">
    <title>vue-cli</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
    <!-- jQuery library -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <!-- Latest compiled JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    </head>
    <body>
        <div id="app"></div>
        <script src="dist/build.js"></script>
    </body>
</html>

Lời kết

:face_with_hand_over_mouth::face_with_hand_over_mouth::face_with_hand_over_mouth: Hãy theo dõi series 2018 – Cùng nhau học VueJS cùng học tập và góp ý để nâng cao kiến thức về VueJs.