> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Event Handling

Lắng nghe DOM event bằng `v-on`. Trong AEM, đây là cách xử lý tương tác trên UI Vue mount lên DOM mà HTL render: click button, submit form, gõ input, đóng/mở panel.

## v-on và cú pháp rút gọn

`v-on:event` lắng nghe một DOM event. Rút gọn bằng `@`.

```html
<button v-on:click="onClick">Click</button>

<!-- rút gọn, dùng phổ biến hơn -->
<button @click="onClick">Click</button>
```

## Inline handler vs method handler

Logic ngắn viết thẳng (inline). Logic phức tạp tách thành method.

```html
<!-- inline: biểu thức ngắn -->
<button @click="counter += 1">Add 1</button>

<!-- method: truyền tên method, không có ngoặc -->
<button @click="greet">Greet</button>

<!-- method có tham số: gọi kèm ngoặc -->
<button @click="say('hi')">Say hi</button>
```

```js
methods: {
  greet(event) {
    // truyền tên method thì handler nhận native event làm tham số đầu
    console.log(this.name, event.target.tagName)
  },
  say(message) {
    console.log(message)
  }
}
```

Khi truyền tên method (không ngoặc), Vue tự truyền native DOM event vào tham số đầu tiên. Khi gọi có ngoặc, phải tự truyền event nếu cần.

## Truy cập native event với $event

Cần cả tham số tùy chỉnh lẫn native event trong inline handler thì dùng biến đặc biệt `$event`.

```html
<button @click="warn('Chưa submit được', $event)">Submit</button>
```

```js
methods: {
  warn(message, event) {
    if (event) event.preventDefault()
    console.log(message)
  }
}
```

## Event modifiers

Thay vì gọi `event.preventDefault()` / `event.stopPropagation()` trong method, dùng modifier để giữ method chỉ lo data logic.

| Modifier   | Tác dụng                                                        |
| ---------- | --------------------------------------------------------------- |
| `.stop`    | `event.stopPropagation()`                                       |
| `.prevent` | `event.preventDefault()`                                        |
| `.self`    | Chỉ chạy khi `event.target` là chính phần tử đó, không phải con |
| `.capture` | Thêm listener ở capture phase                                   |
| `.once`    | Chỉ chạy tối đa 1 lần                                           |
| `.passive` | Báo browser không gọi preventDefault, tối ưu scroll (mobile)    |

```html
<!-- chặn reload trang khi submit form -->
<form @submit.prevent="onSubmit"></form>

<!-- chặn nổi bọt sự kiện -->
<a @click.stop="doThis"></a>

<!-- chain nhiều modifier -->
<a @click.stop.prevent="doThat"></a>

<!-- chỉ dùng modifier, không cần handler -->
<form @submit.prevent></form>
```

Thứ tự modifier quan trọng vì code sinh ra theo đúng thứ tự. `@click.prevent.self` chặn mọi click, còn `@click.self.prevent` chỉ chặn click trên chính phần tử.

Không dùng `.passive` chung với `.prevent`: `.prevent` sẽ bị bỏ qua.

## Key modifiers

Lọc theo phím khi nghe keyboard event. Dùng tên phím từ `KeyboardEvent.key` chuyển sang kebab-case.

```html
<!-- chỉ submit khi nhấn Enter -->
<input @keyup.enter="submit">

<!-- phím PageDown -->
<input @keyup.page-down="onPageDown">
```

## System modifier keys

Chỉ chạy handler khi đang giữ phím hệ thống: `.ctrl`, `.alt`, `.shift`, `.meta` (`meta` là Windows key trên Windows, Command trên Mac).

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

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

`.exact` kiểm soát chính xác tổ hợp phím:

```html
<!-- chạy kể cả khi giữ thêm Alt/Shift -->
<button @click.ctrl="onClick">A</button>

<!-- chỉ chạy khi đúng Ctrl, không phím nào khác -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- chỉ chạy khi không giữ phím hệ thống nào -->
<button @click.exact="onClick">A</button>
```

Mouse button modifier: `.left`, `.right`, `.middle`.

## Tips cho AEM

Vue mount lên DOM HTL render sẵn, event handler hoạt động bình thường trên cây component đó.

```html
<!-- HTL mount point -->
<div id="filter-bar" data-endpoint="${model.searchEndpoint}"></div>
```

```js
new Vue({
  el: '#filter-bar',
  data() {
    return {
      endpoint: this.$el.dataset.endpoint,
      keyword: ''
    }
  },
  methods: {
    onSearch() {
      // gọi API tới endpoint cấu hình từ dialog
      console.log('search', this.keyword, this.endpoint)
    }
  }
})
```

```html
<!-- template Vue -->
<form @submit.prevent="onSearch">
  <input v-model="keyword" @keyup.enter="onSearch">
  <button type="submit">Tìm</button>
</form>
```

Lưu ý: chỉ gắn event lên phần tử nằm trong cây Vue đã mount. Phần HTML do HTL render ngoài mount point không nhận được `v-on`, cần xử lý bằng JS thuần hoặc đưa vào trong component.
