> 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.

# Handling Edge Cases

Các kỹ thuật ít dùng nhưng cần biết khi gặp tình huống đặc biệt. Phần lớn nên hạn chế dùng vì phá vỡ luồng dữ liệu chuẩn (props xuống, events lên).

## Truy cập instance khác

### refs

`ref` lấy tham chiếu trực tiếp tới DOM element hoặc child component instance. Chỉ truy cập sau khi đã mount, dùng cho thao tác bắt buộc (focus input, gọi method của thư viện).

```vue
<template>
  <input ref="search">
  <child-form ref="form"></child-form>
</template>

<script>
export default {
  mounted() {
    this.$refs.search.focus()
    this.$refs.form.reset() // gọi method của child
  }
}
</script>
```

`$refs` không reactive, không dùng trong template hay computed.

### $parent / $root

`$parent` truy cập instance cha, `$root` truy cập root. Dễ tạo coupling khó bảo trì, ưu tiên props/events thay thế. Tránh dùng trừ khi thật cần.

## Dependency injection: provide / inject

Truyền dữ liệu xuống component con sâu nhiều cấp mà không phải truyền props qua từng tầng.

```js
// component tổ tiên
export default {
  provide() {
    return { theme: 'dark' }
  }
}
```

```js
// component con bất kỳ phía dưới
export default {
  inject: ['theme'],
  mounted() {
    console.log(this.theme) // 'dark'
  }
}
```

Lưu ý: `inject` mặc định không reactive. Dùng cho config tĩnh (theme, locale), không dùng cho state thay đổi liên tục.

## Programmatic event listeners

Tự quản lý listener trên instance bằng `$on`, `$once`, `$off`. Hữu ích khi đăng ký listener cho thư viện third-party và cần cleanup.

```js
mounted() {
  this.picker = new Pikaday({ /* ... */ })
  this.$once('hook:beforeDestroy', () => {
    this.picker.destroy()
  })
}
```

## Recursive component

Component có thể tự gọi chính nó qua `name`, dùng render cấu trúc cây (menu, comment lồng nhau). Phải có điều kiện dừng để tránh vòng lặp vô hạn.

```vue
<!-- TreeItem.vue -->
<template>
  <li>
    {{ item.label }}
    <ul v-if="item.children && item.children.length">
      <tree-item
        v-for="child in item.children"
        :key="child.id"
        :item="child" />
    </ul>
  </li>
</template>

<script>
export default {
  name: 'TreeItem',
  props: { item: Object }
}
</script>
```

## Force update và v-once

`this.$forceUpdate()` ép render lại, gần như không bao giờ cần nếu state đúng reactive. Nếu phải dùng, thường là dấu hiệu data không reactive (xem note Vue Instance về `$set`).

`v-once` render một lần rồi cache, bỏ qua cập nhật sau đó. Dùng cho nội dung tĩnh nhiều để tối ưu.

```html
<span v-once>{{ neverChanges }}</span>
```

## Tóm tắt khi nào dùng

| Kỹ thuật            | Dùng khi                     | Cảnh báo                      |
| ------------------- | ---------------------------- | ----------------------------- |
| `$refs`             | Thao tác DOM/method bắt buộc | Không reactive, chỉ sau mount |
| `$parent` / `$root` | Hầu như không nên            | Coupling chặt                 |
| provide/inject      | Config tĩnh xuống sâu        | Mặc định không reactive       |
| `$on/$once/$off`    | Cleanup listener third-party | Tự quản lý vòng đời           |
| Recursive component | Cấu trúc cây                 | Phải có điều kiện dừng        |
| `$forceUpdate`      | Gần như không                | Thường do data sai reactive   |
| `v-once`            | Nội dung tĩnh                | Không cập nhật lại            |

## Tips cho AEM

Tình huống hay gặp: tích hợp thư viện third-party đã có sẵn trong project AEM (carousel, datepicker, chart). Khởi tạo trong `mounted` dùng `$refs`, và huỷ trong `beforeDestroy` để tránh memory leak khi component bị remove (vd khi điều hướng hoặc author xoá component).

```vue
<template>
  <div ref="carousel"></div>
</template>

<script>
export default {
  mounted() {
    this.instance = new ThirdPartyCarousel(this.$refs.carousel)
  },
  beforeDestroy() {
    if (this.instance) this.instance.destroy()
  }
}
</script>
```
