Ở phần trước chúng ta đã được tìm hiểu về khái niệm component và các sử dụng componet trong Vue.js như thế nào rồi. Ở phần cuối của bài trước mình cũng có giới thiệu qua với mọi người về cách thức xử lý dữ liệu, sự kiện giữa 2 component với nhau cụ thể: "props down hay events up - parent truyền props cho child và child phản hồi lại cho parent bằng event." Bài này chúng ta sẽ cùng tìm hiểu về props và event này xem nó là gì và xử lý như nào...
1, Props.
Passing Data with Props
Trong Vue.js thì tất cả các component đều có isolated scope ( phạm vi cô lập ) riêng. Điều này có nghĩa là bạn không thể (và cũng không nên) tham chiếu trực tiếp các dữ liệu ở component cha ( parent ) vào trong component con (child). Mà thay vào đó chúng ta sẽ phải truyền chúng qua props.
Props ở đây là một attribute tùy chỉnh dùng để truyền data từ component parent đến các component child, và để có thể nhận được dữ liệu từ component parent thì component child phải khai báo prop
mà nó mong muốn nhận được từ component parent, bằng cách đặt tên prop ở trong props
scope.
VD:
<div id="app">
<child message="Toidicode.com"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['message'],
template: '<h1>{{ message }}</h1>',
});
var app = new Vue({
el: '#app',
});
</script>
camelCase vs. kebab-case
Ở trong HTML thì các nó sẽ không phân biệt chữ hoa hay chữ thường (có nghĩa là onclick = OnCliCk), nên nếu như bạn muốn sự dụng kiểu đặt tên kiểu camelCase cho prop thì ở lúc gọi componet bạn phải chuyển đối nó về kebad-case.
VD:
<div id="app">
<child my-say="Toidicode.com - Học lập trình online miễn phí"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['mySay'],
template: '<h1>{{ mySay }}</h1>',
});
var app = new Vue({
el: '#app',
});
</script>
Dynamic Props
Và cũng tương tự như đối với attribute, bạn cũng có thể sử dụng v-bind để ràng buộc prop data trên cấp cha . Và khi dữ liệu ở cấp cha thay đổi thì cấp con cũng thay đổi theo.
VD:
<div id="app">
<p>
<input type="text" v-model="message">
</p>
<child v-bind:my-say="message"></child>
<!-- hoạc bạn cũng có thể sử dụng cú pháp thu gọn là
:my-say="message"
-->
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['mySay'],
template: '<h1>{{ mySay }}</h1>',
});
var app = new Vue({
el: '#app',
data: {
message : null
}
});
</script>
Literal vs Dynamic
Nếu như bạn muốn truyền dữ liệu dạng số (number) vào component thì bạn sẽ phải sử dụng v-bind
, còn nếu bạn dùng prop thông thường thì nó sẽ nhận là string. Hãy xem ví dụ trên để kiểm chứng:
VD:
<div id="app">
<child my-say="1"></child>
<child v-bind:my-say="1"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['mySay'],
template: '<h1>{{ typeof(mySay) }}</h1>',
});
var app = new Vue({
el: '#app',
});
</script>
One-Way Data Flow
Tất cả các props trong Vue.js đề là one-way-down nó dùng để binding data giữa cấp cha và cấp con. Khi dữ liệu ở cấp cha thay đổi thì nó sẽ được binding xuống cấp con. Nhưng khi cấp con thay đổi thì props sẽ không làm thay đổi được cấp cha.
Ngoài ra nếu như dữ liệu ở cấp cha (component parent) thay đổi thì cấp con (component child) sẽ cập nhật và lấy giá trị mới nhất của dữ liệu thay đổi đó. Điều đó tương đương với việc bạn không thể nào thay đổi một prop bên trong component con. Nếu như bạn cố tình thực hiện điều đó, thì Vue.js sẽ đưa ra cảnh báo cho bạn ở trong console.
Nhưng chúng ta hoàn toàn có thể thay đổi nó một cách gián tiếp bằng 2 cách sau:
- Khởi tạo một data scope chứa prop đó, từ đó bạn có thể thay đổi dữ liệu thông qua data scope.
VD:
<div id="app">
<child my-say="Hello!"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['mySay'],
data : function () {
return { dataSay : this.mySay + " This is dataSay!"};
},
template: '<h1>{{ dataSay }}</h1>',
});
var app = new Vue({
el: '#app',
});
</script>
- Đề xuất prop như một dữ liệu cần được thay đổi.
VD:
<div id="app">
<child my-say="hello!"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
props: ['mySay'],
computed : {
convertSay: function () {
return this.mySay.trim().toUpperCase();
}
},
template: '<h1>{{ convertSay }}</h1>',
});
var app = new Vue({
el: '#app'
});
</script>
Prop Validation
Trong một vài trường hợp mà bạn muốn ràng buộc kiểu dữ liệu của một prop
, hay xác định giá trị mặc định của nó thid Vue.js cũng hỗ trợ bạn điều đó.
Cú pháp xác định kiểu dữ liệu:
props: {
propName: typeName
}
hoặc
props: {
propName: [typeName1,typeName2,...typeNamen]
}
hoặc
props: {
propName: {
type: typeName
}
}
Trong đó:
propName
là tên của prop mà bạn muốn ràng buộc.typeName
là kiểu dữ liệu bạn muốn xác định. Các kiểu dữ liệu gồm có:- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
VD:
Vue.component('ex',{
props: {
name: String,
class: [Number, String],
point: {
type: Number
}
}
});
Hoặc bạn cũng có thể tự custom validate cho riêng mình bằng cách sử dụng validator
key trong object của prop
bạn cần ràng buộc.
VD:
Vue.component('ex',{
props: {
point: {
validator: function (value) {
return value > 10;
}
}
}
});
Và bạn cũng có thể thiết lập giá trị mặc định cho prop bằng default
scope.
VD:
Vue.component('ex',{
props: {
point: {
type: Number,
default: 5,
}
}
});
Để xác định một prop bắt buộc phải là required
thì bạn chỉ cần thiết lập required
scope thành true là được.
VD:
Vue.component('ex',{
props: {
point: {
type: Number,
required: true,
}
}
});
Nếu như dữ liệu validate không qua, mà bạn đang mode chương trình ở chế độ development thì Vue.js sẽ thông báo cho bạn ở cửa sổ console.
Và dữ liệu trong default scope hay trong validator scope sẽ phải là dữ liệu có sẵn (không dùng được dữ liệu của data
, computed
, methods
) vì validate sẽ được thực hiện trước khi component instance.
2, Non-Prop Attributes.
Trong Vue.js, Non-Prop attribute là một attribute được chuyển đến component, nhưng nó sẽ không có được tính chất prop đã định nghĩa.
Mặc dù các prop đã được xác định rõ ràng để chuyển thông tin đến child component, nhưng do chính tác giả của thư viện component trong Vue.js cũng không thể lường trước được bối cảnh của chúng ta sử dụng. Đó chính là lý do mà component cho phép chúng ta xác định bất kỳ một attribute nào cũng có thể được xác định trên component parent.
3, Custom Events.
-Như mình đã trình bày ở cuối phần một là component cha binding data xuống component con thông qua props và component con thông báo lại cho component cha thông qua các events. Vậy thì ở phần này chúng ta sẽ cùng nghiên cứu xem chúng ta có các cách gửi events nào nhé.
Using v-on
Trong Vue.js, tất cả các Vue instance đều phải implements một event interface. Đều đó tương đương với việc chúng ta có thể:
- Lắng nghe tất cả các sự kiện thông qua
$on(eventName)
. - Kích hoạt các sự kiện thông qua
$emit(eventName)
.
Nhưng các event này không giống như chúng ta sử dụng các eventTarget trên trình duyệt. Có nghĩa là
$on
sẽ khác vớiaddEventListener
và$emit
sẽ khác vớidispatchEvent
.
Ngoài ra thì các component cha cũng có thể lắng nghe các sự kiện từ component con gửi lên thông qua v-on directive.
Nhưng bạn không thể sử dụng $on để lắng nghe các sự kiện được emmited ở component con. Mà phải sử dụng v-on
directive như ví dụ sau:
VD:
<div id="app">
<p>{{ count }}</p>
<child v-on:counter="countPlus"></child>
<child v-on:counter="countPlus"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
template: '<button v-on:click="numberPlus">{{ count }}</button>',
data : function () {
return {count: 0};
},
methods: {
numberPlus: function ()
{
this.count += 1;
this.$emit('counter')
}
}
});
var app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
countPlus: function ()
{
this.count += 1;
}
}
});
</script>
Binding Native Events to Components.
-Nếu như bạn không muốn sử dụng emit
name sự kiện mà bạn muốn lắng nghe sự kiện gốc trên thành phần gốc thì bạn có thể sử dụng .native
modifier.
VD:
<div id="app">
<p>{{ count }}</p>
<child v-on:click.native="countPlus"></child>
</div>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
template: '<button>Click</button>'
});
var app = new Vue({
el: '#app',
data: {
count: 0
},
methods: {
countPlus: function ()
{
this.count += 1;
}
}
});
</script>
4, x-template.
Từ trước đến giờ chúng ta đã được học về khai báo template bằng cách khai báo nó ở trong template scope rồi, nhưng sẽ ra sao nếu template đó không chỉ đơn giản là một hai dòng nữa? Trong Vue.js có cung cấp cho chúng ta khai báo template ở trong cặp thẻ script
có type là x-template
. Với cú pháp như sau:
<script type="text/x-template" id="template-id">
//content template
</script>
Trong đó: chúng ta cần xác định id
cho template để có thể gọi nó ở trong component template.
Lúc này ở trong template
scope các bạn chỉ cần xác định id của x-template. Với cú pháp:
Vue.component('component-tag', {
template: '#template-id'
});
VD:
<div id="app">
<child></child>
</div>
<script type="text/x-template" id="h1-template">
<h1>Template được khai báo trong x-template</h1>
</script>
<script type="text/javascript"></script>
<script type="text/javascript">
Vue.component('child', {
template: '#h1-template'
});
var app = new Vue({
el: '#app',
});
</script>
5, Lời kết.
-Như vậy hết phần này mình đã giới thiệu xong với mọi người những gì cơ bản nhất trong component rồi. Còn một số phần cơ bản mình sẽ giới thiệu tiếp với mọi người ở phần nâng cao sau:
Đăng ký nhận tin.
Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!
Hay quá anh! Viết tiếp đi ạ :D
Trung Nguyen Pham
7 năm trước
mình k hiểu cái ví dụ Binding Native Events to Components ?? @click.native với @click khác nhau gì vậy :D
PHAM DAT
6 năm trước