Bài 14: Event handling với VueJS

Chào mừng các bạn quay trở lại với series học VueJS với Laravel của mình, ở bài trước mình đã hướng dẫn các bạn về cách binding form input, bài này chúng ta sẽ cùng tìm hiểu cách xử lý các sự kiện khi người dùng tương tác như click hay bấm các phím trong VueJS nhé.

Để bắt các sự kiện này ta dùng v-on hoặc mình thường dùng kiểu short-hand@ (trong bài này mình dùng cách viết shorthand nhé). Ở bài này ta tạo ra một file mới EventHandling.vue sau đó khai báo component này trong app.jswelcome.blade.php. Nhớ chạy php artisan servenpm run watch nhé mọi người.

Ở bài này nội dung khá là đơn giản và mình nghĩ nó cũng khá dễ hiểu cho các bạn, nên chúng ta sẽ chủ yếu làm qua các ví dụ để hiểu nhé.

Click events

Xét ví dụ sau:

<template>
    <div class="event-handling">
        <div>Current Number: {{ counter }}</div>
        <button @click="counter++">CLick</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                counter: 0
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Ở đây ta thiết lập sự kiện khi người dùng click vào button thì tăng giá trị của counter lên 1 bằng cách dùng @click như hình, và chúng ta in giá trị của counter ra ngoài màn hình. Các bạn có thể load lại trang và thử bằng cách click vào button để xem giá trị của counter thay đổi như thế nào nhé.

Bên cạnh đó một cách thường dùng là ta click để thực hiện gọi một phương thức nào đó, để làm như vậy ta xem ví dụ sau nhé:

<template>
    <div class="event-handling">
        <div>Current Number: {{ counter }}</div>
        <button @click="increaseCounter">CLick</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            increaseCounter() {
                this.counter += 10
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Ở trên mỗi khi người dùng click vào button thì phương thức increaseCounter được gọi và tăng giá trị của counter lên 10, load lại trang và thử lại nhé các bạn 😉

Nếu muốn truyền tham số vào phương thức lúc button được click ta làm như sau nhé:

<template>
    <div class="event-handling">
        <div>Current Number: {{ counter }}</div>
        <button @click="increaseCounter(50)">CLick</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            increaseCounter(number) {
                this.counter += number
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Các chỉ định đi cùng với click

Trong Vue thay vì phải gọi event.preventDefault() hay event.stopPropagation() thì ta có thể sử dụng chúng một cách khá đơn giản như những ví dụ sau.

click.stop

<template>
    <div class="event-handling">
        <div @click="alertHello">
            <button @click="alertWelcome">CLick</button>
        </div>
    </div>
</template>

<script>
    export default {
        methods: {
            alertHello() {
                alert('Hello')
            },
            alertWelcome() {
                alert('Welcome')
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Ở ví dụ trên ta có button ở trong thẻ div sau đó ta đồng thời set @click cho cả button và thẻ div này. Khi đó mỗi lần click vào button thì đồng thời 2 sự kiện click sẽ được gọi (@click của button được gọi trước). Điều đó sẽ alert ra màn hình 2 lần. Để ngăn chặn điều này ta làm như sau. Sửa lại một chút ở button:

<button @click.stop="alertWelcome">CLick</button>

Ta thêm chỉ định stop vào @click cũng giống như việc gọi event.stopPropagation(). Giờ đây khi click vào button thì chỉ duy nhất sự kiện click trong button đó được gọi mà thôi. Các bạn load lại trang và làm thử nhé

click.prevent

Chúng ta cùng xem ví dụ sau:

<template>
    <div class="event-handling">
        <form>
            <button type="submit">Submit</button>
        </form>
    </div>
</template>

<script>
    export default {
    }
</script>

<style lang="scss" scoped>
</style>

Bình thường khi khai báo form như trên, mỗi khi click vào button submit thì trang sẽ bị load lại. Và để điều này không xảy ra thì ta sửa lại button như sau:

<template>
    <div class="event-handling">
        <form @submit.prevent>
            <button type="submit">Submit</button>
        </form>
    </div>
</template>

<script>
    export default {
    }
</script>

<style lang="scss" scoped>
</style>

Thử load lại trang và các bạn có thể thấy khi click vào Submit giở đây trang đã không còn load lại nữa.

Để thực hiện một hành động khi submit các bạn có thể làm như sau:

<form @submit.prevent="sayWelcome">
    <button type="submit">Submit</button>
</form>

Hoặc thêm trực tiếp prevent vào button như sau:

<template>
    <div class="event-handling">
        <form>
            <button type="submit" @click.prevent="sayWelcome">Submit</button>
        </form>
    </div>
</template>

<script>
    export default {
        methods: {
            sayWelcome() {
                alert(1)
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Nếu muốn thực hiện stop và prevent đồng thời ta làm như sau:

<button type="submit" @click.stop.prevent="sayWelcome">Submit</button>

click.once

Khi muốn chỉ thực hiện hành động một lần khi click ta làm như sau:

<button type="submit" @click.once="sayWelcome">Submit</button>

click.capture

Ở trên với ví dụ button trong thẻ div khi để bình thường không có stop thì hành động trong element button là con của div được gọi trước. Để thực hiện ngược lại, tức khi click vào button thì phương thức trong thẻ div được gọi trước sau đó đến con của nó là button thì ta làm như sau:

<template>
    <div class="event-handling">
        <div @click.capture="alertHello">
            <button @click="alertWelcome">CLick</button>
        </div>
    </div>
</template>

<script>
    export default {
        methods: {
            alertHello() {
                alert('Hello')
            },
            alertWelcome() {
                alert('Welcome')
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Các bạn có thể load lại trang và thử click vào button, sẽ thấy alert ra Hello trước sau đó mới đến Welcome.

click.self

Một cách khác để ngăn chặn khi click vào button mà phương thức trong thẻ div cũng được gọi, thay vì sử dụng click.stop cho button đó là sử dụng click.self cho div, khi đó, khi click vào con của div thì hành động trong thẻ div sẽ không được gọi.

<template>
    <div class="event-handling">
        <div @click.self="alertHello">
            <button @click="alertWelcome">CLick</button>
        </div>
    </div>
</template>

<script>
    export default {
        methods: {
            alertHello() {
                alert('Hello')
            },
            alertWelcome() {
                alert('Welcome')
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Các bạn có thể tải lại trang và ta có thể kết quả giống như cách sử dụng click.stop

click.passive

<!-- 
hành vi mặc định của sự kiện scroll (cuộn trang) sẽ xảy ra 
ngay lập tức, thay vì đợi `onScroll` hoàn tất.
-->
<div v-on:scroll.passive="onScroll">...</div>

Key events

Khi muốn lắng nghe sự kiện khi người dùng tương tác với các nút trên bàn phím. Ta có thể sử dụng một số cách như sau:

<template>
    <div class="event-handling">
        <input @keyup.enter="alertHello">

        <input @keyup.13="alertHello">
    </div>
</template>

<script>
    export default {
        methods: {
            alertHello() {
                alert('Hello')
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Bên cạnh đó Vue cung cấp một số sự kiện mặc định cho một số nút như sau:

.enter
.tab
.delete (hiệu lực với cả 2 nút  “Delete” và “Backspace”)
.esc
.space
.up
.down
.left
.right

Để tuỳ chỉnh ta xem ví sau và xem mình giải thích bên dưới nhé:

app.js

Vue.config.keyCodes.sayHello = 13

EventHandling.vue

<template>
    <div class="event-handling">
        <input @keyup.sayHello="alertHello">
    </div>
</template>

<script>
    export default {
        methods: {
            alertHello() {
                alert('Hello')
            }
        }
    }
</script>

<style lang="scss" scoped>
</style>

Ở trên thay vì gọi @keyup.enter ta thiết lập trong app.js cách gọi khác là @keyup.sayHello với keycode là 13(chính là phím enter). Các bạn có thể lấy keycode của phím bất kì tại đây

System modifiers

Vue hỗ trợ chúng ta một số sự kiện ấn tổ hợp phím như sau:

.ctrl
.alt
.shift
.meta

chú ý: meta là phím command trên Mac, là hím cửa sổ trên Win Ví dụ:

<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

chú ý:

Ở ví dụ trên ta dùng @keyup.alt.67 thay cho tổ hợp phím AltC, thì clear sẽ được gọi khi ta thả phím C ra trước, nếu ta thả phím Alt ra trước sự kiện sẽ không được gọi.

exact

exact được sử dụng khi ta muốn thực hiện một hành động nào đó chỉ khi người dùng bấm chính xác các nút hay tổ hợp nút như ta thiết lập mà không có sự tác động đồng thời của các system modifiers như ta đã nói ở phần trên.

<!-- hành động bên dưới được gọi ngay cả khi các nút khác được bấm đồng thời như Alt hay Shift -->
<button @click.ctrl="onClick">A</button>

<!-- chỉ thực hiện khi bấm chính xác tổ hợp click+ctrl -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- Chỉ thực hiện khi click -->
<button @click.exact="onClick">A</button>

Mouse events

Vue cũng cấp cho chúng ta sự kiện khi click chuột như sau:

.left <!-- khi chuột trái click -->
.right <!-- khi chuột phải click -->
.middle <!-- khi bấm con lăn chuột  -->

Phần này các bạn có thể tự làm ví dụ của riêng mình nhé, mình nghĩ sẽ không khó khăn lắm cho các bạn (nếu có gì các bạn comment bên dưới nhé).

Kết luận

Qua bài này mong rằng các bạn có thể biết được về cách xử lsy các sự kiện khi người dùng tương tác với bàn phím hay chuột trong VueJS, từ đó áp chụng tuỳ chỉnh vào các mục đích cá nhân khác nhau.

Ở bài tiếp theo chúng ta sẽ cùng tìm hiểu về CSS scoped trong VueJS nhé.

Cám ơn các bạn đã theo dõi series của mình, nếu có gì thắc mắc các bạn để lại dưới comment nhé ^^!