> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# Chỉ báo tiến trình

<Warning>Bạn đang xem tài liệu Inertia.js v2. Inertia.js v3 đã được phát hành và hiện là phiên bản mặc định. Hãy xem [hướng dẫn nâng cấp](/v3/getting-started/upgrade-guide) để bắt đầu.</Warning>

Vì request Inertia được thực hiện qua XHR, trình duyệt thường không hiển thị chỉ báo tải khi điều hướng từ trang này sang trang khác. Để giải quyết, Inertia hiển thị progress indicator ở đầu trang mỗi khi bạn thực hiện một Inertia visit. Tuy nhiên, [asynchronous request](#visit-options) không hiển thị progress indicator trừ khi được cấu hình rõ ràng.

Tất nhiên, nếu muốn, bạn có thể tắt loading indicator mặc định của Inertia và tự triển khai. Chúng ta sẽ trình bày cả hai cách bên dưới.

## Mặc định

Progress indicator mặc định của Inertia là một lớp bọc nhẹ quanh thư viện [NProgress](https://ricostacruz.com/nprogress/). Bạn có thể tùy chỉnh thông qua property `progress` của hàm `createInertiaApp()`.

```js theme={null}
createInertiaApp({
    progress: {
        // The delay after which the progress bar will appear, in milliseconds...
        delay: 250,
        // The color of the progress bar...
        color: '#29d',
        // Whether to include the default NProgress styles...
        includeCSS: true,
        // Whether the NProgress spinner will be shown...
        showSpinner: false,
    },
    // ...
})
```

Bạn có thể tắt loading indicator mặc định của Inertia bằng cách đặt property `progress` thành `false`.

```js theme={null}
createInertiaApp({
    progress: false,
    // ...
})
```

## Truy cập bằng code

Khi cần kiểm soát progress indicator bên ngoài request Inertia, ví dụ khi gửi request bằng Axios hoặc thư viện khác, bạn có thể dùng trực tiếp các phương thức progress của Inertia.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { progress } from '@inertiajs/vue3'

  progress.start()      // Begin progress animation
  progress.set(0.25)    // Set to 25% complete
  progress.finish()     // Complete and fade out
  progress.reset()      // Reset to start
  progress.remove()     // Complete and remove from DOM
  progress.hide()       // Hide progress bar
  progress.reveal()     // Show progress bar

  progress.isStarted()  // Returns boolean
  progress.getStatus()  // Returns current percentage or null
  ```

  ```js React icon="react" theme={null}
  import { progress } from '@inertiajs/react'

  progress.start()      // Begin progress animation
  progress.set(0.25)    // Set to 25% complete
  progress.finish()     // Complete and fade out
  progress.reset()      // Reset to start
  progress.remove()     // Complete and remove from DOM
  progress.hide()       // Hide progress bar
  progress.reveal()     // Show progress bar

  progress.isStarted()  // Returns boolean
  progress.getStatus()  // Returns current percentage or null
  ```

  ```js Svelte icon="s" theme={null}
  import { progress } from '@inertiajs/svelte'

  progress.start()      // Begin progress animation
  progress.set(0.25)    // Set to 25% complete
  progress.finish()     // Complete and fade out
  progress.reset()      // Reset to start
  progress.remove()     // Complete and remove from DOM
  progress.hide()       // Hide progress bar
  progress.reveal()     // Show progress bar

  progress.isStarted()  // Returns boolean
  progress.getStatus()  // Returns current percentage or null
  ```
</CodeGroup>

Các phương thức `hide()` và `reveal()` phối hợp để tránh xung đột khi nhiều phần code cùng cần kiểm soát khả năng hiển thị progress. Mỗi lần gọi `hide()` sẽ tăng một bộ đếm nội bộ, còn `reveal()` giảm bộ đếm đó. Progress bar chỉ xuất hiện khi bộ đếm trở về 0.

Tuy nhiên, `reveal()` nhận tham số tùy chọn `force` để bỏ qua bộ đếm này. Inertia dùng chính cơ chế đó ở bên trong để ẩn progress khi prefetch nhưng vẫn đảm bảo progress xuất hiện khi điều hướng thực tế.

```js theme={null}
progress.hide()    // Counter = 1, bar hidden
progress.hide()    // Counter = 2, bar still hidden
progress.reveal()  // Counter = 1, bar still hidden
progress.reveal()  // Counter = 0, bar now visible

// Force reveal bypasses the counter
progress.reveal(true)
```

Nếu bạn đã tắt progress indicator bằng `progress: false` trong `createInertiaApp()`, các phương thức lập trình này sẽ không hoạt động.

## Tùy chỉnh

Bạn cũng có thể tự thiết lập page loading indicator tùy chỉnh bằng [events](/v2/advanced/events) của Inertia. Hãy xem cách thực hiện với thư viện [NProgress](https://ricostacruz.com/nprogress/) làm ví dụ.

Trước tiên, hãy tắt loading indicator mặc định của Inertia.

```js theme={null}
createInertiaApp({
    progress: false,
    // ...
})
```

Tiếp theo, cài thư viện NProgress.

```bash theme={null}
npm install nprogress
```

Sau khi cài đặt, bạn cần thêm [style](https://github.com/rstacruz/nprogress/blob/master/nprogress.css) của NProgress vào dự án. Bạn có thể dùng bản style được host trên CDN.

```html theme={null}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" />
```

Sau đó, import cả `NProgress` và `router` của Inertia vào ứng dụng.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/vue3'
  ```

  ```js React icon="react" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/react'
  ```

  ```js Svelte icon="s" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/svelte'
  ```
</CodeGroup>

Tiếp theo, thêm event listener `start`. Listener này sẽ hiển thị progress bar khi một Inertia visit mới bắt đầu.

```js theme={null}
router.on('start', () => NProgress.start())
```

Cuối cùng, thêm event listener `finish` để ẩn progress bar khi page visit kết thúc.

```js theme={null}
router.on('finish', () => NProgress.done())
```

Vậy là xong. Giờ khi điều hướng giữa các trang, progress bar sẽ tự động được thêm vào và loại bỏ khỏi trang.

### Xử lý visit bị hủy

Mặc dù phần triển khai progress tùy chỉnh này hoạt động tốt với các page visit hoàn tất bình thường, sẽ tốt hơn nếu xử lý cả visit bị hủy. Thứ nhất, với visit bị gián đoạn (bị hủy do một visit mới bắt đầu), progress bar chỉ nên được reset về vị trí bắt đầu. Thứ hai, với visit bị hủy thủ công, progress bar nên được loại bỏ khỏi trang ngay lập tức.

Chúng ta có thể thực hiện bằng cách kiểm tra object `event.detail.visit` được cung cấp cho sự kiện finish.

```js theme={null}
router.on('finish', (event) => {
    if (event.detail.visit.completed) {
        NProgress.done()
    } else if (event.detail.visit.interrupted) {
        NProgress.set(0)
    } else if (event.detail.visit.cancelled) {
        NProgress.done()
        NProgress.remove()
    }
})
```

### Tiến trình tải file

Hãy tiến thêm một bước. Khi file đang được tải lên, sẽ rất hữu ích nếu loading indicator phản ánh đúng tiến trình upload. Bạn có thể làm việc này bằng sự kiện `progress`.

```js theme={null}
router.on('progress', (event) => {
    if (event.detail.progress.percentage) {
        NProgress.set((event.detail.progress.percentage / 100) * 0.9)
    }
})
```

Giờ thay vì progress bar chỉ tăng dần mang tính ước lượng trong lúc file được tải lên, vị trí của nó sẽ thực sự cập nhật theo tiến trình request. Ở đây chúng ta giới hạn tiến trình ở 90% vì vẫn còn phải chờ response từ máy chủ.

### Trì hoãn loading indicator

Phần cuối cùng chúng ta sẽ triển khai là độ trễ trước khi hiển thị loading indicator. Thông thường nên trì hoãn việc hiển thị cho đến khi request kéo dài hơn 250–500 mili giây. Điều này tránh loading indicator xuất hiện liên tục ở các page visit rất nhanh, vốn có thể gây rối mắt.

Để triển khai độ trễ, chúng ta sẽ dùng các hàm `setTimeout` và `clearTimeout`. Trước tiên, hãy định nghĩa một biến để theo dõi timeout.

```js theme={null}
let timeout = null
```

Tiếp theo, cập nhật listener `start` để tạo timeout mới, hiển thị progress bar sau 250 mili giây.

```js theme={null}
router.on('start', () => {
    timeout = setTimeout(() => NProgress.start(), 250)
})
```

Sau đó, cập nhật listener `finish` để xóa mọi timeout còn tồn tại nếu page visit hoàn tất trước khi timeout kết thúc.

```js theme={null}
router.on('finish', (event) => {
    clearTimeout(timeout)
    // ...
})
```

Trong listener `finish`, chúng ta cần xác định progress bar đã thực sự bắt đầu hiển thị tiến trình hay chưa; nếu không, vô tình chúng ta sẽ khiến nó xuất hiện trước khi timeout kết thúc.

```js theme={null}
router.on('finish', (event) => {
    clearTimeout(timeout)

    if (!NProgress.isStarted()) {
        return
    }
    // ...
})
```

Cuối cùng, chúng ta cũng cần thực hiện cùng phép kiểm tra đó trong listener `progress`.

```js theme={null}
router.on('progress', event => {
    if (!NProgress.isStarted()) {
        return
    }
    // ...
})
```

Vậy là bạn đã có một loading indicator tùy chỉnh đẹp mắt cho trang.

### Ví dụ hoàn chỉnh

Để tiện tham khảo, dưới đây là toàn bộ source code của phiên bản cuối cùng cho loading indicator tùy chỉnh.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/vue3'

  let timeout = null

  router.on('start', () => {
      timeout = setTimeout(() => NProgress.start(), 250)
  })

  router.on('progress', (event) => {
      if (NProgress.isStarted() && event.detail.progress.percentage) {
          NProgress.set((event.detail.progress.percentage / 100) * 0.9)
      }
  })

  router.on('finish', (event) => {
      clearTimeout(timeout)

      if (!NProgress.isStarted()) {
          return
      }

      if (event.detail.visit.completed) {
          NProgress.done()
      } else if (event.detail.visit.interrupted) {
          NProgress.set(0)
      } else if (event.detail.visit.cancelled) {
          NProgress.done()
          NProgress.remove()
      }
  })
  ```

  ```js React icon="react" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/react'

  let timeout = null

  router.on('start', () => {
      timeout = setTimeout(() => NProgress.start(), 250)
  })

  router.on('progress', (event) => {
      if (NProgress.isStarted() && event.detail.progress.percentage) {
          NProgress.set((event.detail.progress.percentage / 100) * 0.9)
      }
  })

  router.on('finish', (event) => {
      clearTimeout(timeout)

      if (!NProgress.isStarted()) {
          return
      }

      if (event.detail.visit.completed) {
          NProgress.done()
      } else if (event.detail.visit.interrupted) {
          NProgress.set(0)
      } else if (event.detail.visit.cancelled) {
          NProgress.done()
          NProgress.remove()
      }
  })
  ```

  ```js Svelte icon="s" theme={null}
  import NProgress from 'nprogress'
  import { router } from '@inertiajs/svelte'

  let timeout = null

  router.on('start', () => {
      timeout = setTimeout(() => NProgress.start(), 250)
  })

  router.on('progress', (event) => {
      if (NProgress.isStarted() && event.detail.progress.percentage) {
          NProgress.set((event.detail.progress.percentage / 100) * 0.9)
      }
  })

  router.on('finish', (event) => {
      clearTimeout(timeout)

      if (!NProgress.isStarted()) {
          return
      }

      if (event.detail.visit.completed) {
          NProgress.done()
      } else if (event.detail.visit.interrupted) {
          NProgress.set(0)
      } else if (event.detail.visit.cancelled) {
          NProgress.done()
          NProgress.remove()
      }
  })
  ```
</CodeGroup>

## Tùy chọn visit

Ngoài các cấu hình trên, Inertia.js cung cấp hai tùy chọn visit để kiểm soát loading indicator theo từng request: `showProgress` và `async`. Các tùy chọn này cho phép kiểm soát chi tiết hơn cách Inertia.js xử lý asynchronous request và quản lý progress indicator.

### ShowProgress

Tùy chọn `showProgress` cho phép kiểm soát chi tiết khả năng hiển thị loading indicator trong khi request đang chạy.

```js theme={null}
router.get('/settings', {}, { showProgress: false })
```

### Async

Tùy chọn `async` cho phép thực hiện asynchronous request mà không hiển thị progress indicator mặc định. Có thể dùng kết hợp với tùy chọn `showProgress`.

```js theme={null}
// Disable the progress indicator
router.get('/settings', {}, { async: true })
// Enable the progress indicator with async requests
router.get('/settings', {}, { async: true, showProgress: true })
```

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v2 chính thức](https://inertiajs.com/docs/v2/advanced/progress-indicators). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
