Router và Navigation

Router và navigation là thành phần quan trọng để tổ chức hệ thống.

Trong dự án này, router và navigation đi chung với nhau. Bạn chỉ cần cấu hình router ở @/router/index.js rồi menu ở sidebar sẽ tự động được tạo ra. Điều này rất tiện lợi và giảm thời gian chỉnh sửa sidebar navigation. Dĩ nhiên bạn sẽ phải cấu hình theo đúng yêu cầu của 1 route

Cấu hình

Sau đây là các tùy chọn cho một route

// Nếu là true, route này sẽ không hiện ra trên sidebar menu
// Ví dụ trang đăng nhập hay trang lỗi
hidden: true

// Nếu noredirect, route này sẽ không click được
redirect: noredirect

// Khi 1 route cha có nhiều hơn 1 route con, tất cả các route con sẽ hiển thị dưới dạng nested (menu con)
// Nhưng khi 1 route cha chỉ có 1 route con, thì menu sẽ hiển thị route con thay cho route cha (menu con chỉ có 1 link, thì sẽ thay thế menu cha)
// Nếu bạn vẫn muốn hiển thị menu cha - con trong khi menu con chỉ có 1 link, thì bạn thiết lập alwaysShow: true
alwaysShow: true

// Tên của route, luôn luôn yêu cầu nếu không chúng ta sẽ có vấn đề với <keep-alive>.
name: 'router-name'

meta: {
  // Nhóm người dùng được phép truy cập vào route này, nếu không thiết lập, tất cả các nhóm đều truy cập được
  roles: ['admin', 'editor'],
  // Quyền hạn để được phép truy cập vào route này, nếu không thiết lập, tất cả người dùng đề truy cập được
  permissions: ['view menu zip', 'manage user'],

  // Tên xuất hiện trên layout (ở Sidebar, Breadcrumbs,...)
  title: 'title'

  // svg icon class
  icon: 'svg-name'

  // Nếu là true, route này sẽ không được "cache" bởi <keep-alive>. Mặc định là false (tất cã route đểu được cache)
  noCache: true

  // Nếu là false, route này sẽ không xuất hiện ở breadcrumb (mặc định là true)
  breadcrumb: false
}

Ví dụ:

{
  path: '/permission',
  component: Layout,
  redirect: '/permission/index',
  alwaysShow: true, // will always show the root menu
  meta: {
    title: 'permission',
    icon: 'lock',
    permissions: ['view menu permission'], // Chỉ cho phép truy cập với những user có quyền 'view menu permission'
  },
  children: [...],
}

Router

Có 2 loại routes: constantRoutes and asyncRoutes.

constantRoutes: bao gồm các routes mà không yêu cầu xác thực truy cập ví dụ như trang login hay các trang lỗi - tóm lại là ai vào cũng được.

asyncRoutes: bao gồm các routes mà yêu cầu xác thực quyền hay nhóm người dùng, và được thêm vào bằng addRoutes. Chi tiết xem thêm ở Quyền và nhóm người dùng.

TIP

Tất cả các trang của Laravue đều sử dụng router lazy loading, chi tiết có thể xem ở đây

Nếu bạn muốn biết thêm về browserHistoryhashHistory, vui lòng xem Build & Deploy.

Các cấu hình khác của vue-router đều được giữ nguyên, bạn có thể tham khảo trực tiếp từ tài liệu của vue-router

Chú ý

Trang lỗi 404 phải được khai báo cuối cùng trong constantRoutes vì các routes khai báo sau 404 sẽ bị chặn bởi 404 và không có tác dụng. Chi tiết có thể tham khảo ở addRoutes when you've got a wildcard route for 404s does not work

Sidebar trong Laravue được thiết kế dựa trên el-menu của element-ui.

Như đã nói ở các tài liệu trước, sidebar được sinh ra tự động bằng cách duyệt các routes và kiểm tra chéo với hệ thống phân quyền, đồng thời hỗ trợ menu "đa cấp".

Code: @/layout/components/Sidebar

Styles mặc định của element-ui đã thay được thay đổi khá nhiều, các bạn có thể vào @/styles/sidebar.scss để xem và thay đổi nếu muốn.

Chú ý quan trọng. Sidebar có 2 loại: submenu (menu con) và el-menu-item (menu trực tiếp). Xem hình dưới đây:

Như đã nói ở trên, sidebar hỗ trợ menu con bằng cách tạo nhiều routes trong children, các routes đó sẽ tự động chuyển thành menu con. Nếu children chỉ có 1 route, thì route đó sẽ thay thế menu cha trừ khi chúng ta bắt buộc nó hiện ở menu con bằng cách đặt alwaysShow: true. Ví dụ:

// Không có sub-menu: children.length===1
{
  path: '/icon',
  component: Layout,
  children: [{
    path: 'index',
    component: ()=>import('svg-icons/index'),
    name: 'icons',
    meta: { title: 'icons', icon: 'icon'},
  }],
},

// Có sub-menu: children.length===1, và alwaysShow===true
{
  path: '/icon',
  component: Layout,
  alwaysShow: true,
  children: [{
    path: 'index',
    component: ()=>import('svg-icons/index'),
    name: 'icons',
    meta: { title: 'icons', icon: 'icon'},
  }],
},

// Có sub=menu: children.length > 1
{
  path: '/components',
  component: Layout,
  name: 'component-demo',
  meta: {
    title: 'components',
    icon: 'component',
  },
  children: [
    { path: 'tinymce', component: () =>import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
    { path: 'markdown', component: () =>import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
  ],
}

Click vào sidebar để refresh route hiện tại

Với cách làm web truyền thống (trước khi có SPA - Single Page Application), khi người dùng click vào menu trên sidebar, webpage sẽ được load lại. Tuy nhiên với VueJS thì điều này thay đổi, vue-router sẽ kiểm tra danh sách routes, nếu URL không đổi thì không có hook nào được trigger, webpage sẽ không thay đổi. Xem thêm thảo luận ở đây.

Chúng ta có thể thay đổi điều này bằng cách thay đổi URL để bắt buộc các hook được trigger và webpage sẽ được reload lại. Ví dụ:

clickLink(path) {
  this.$router.push({
    path,
    query: {
      //Ensure that each click, query is not the same
      //to ensure that refresh the view
      t: +new Date(),
    }
  })
}

Lưu ý: Đừng quên thêm key duy nhất vào router-view, ví dụ: <router-view :key="$route.path"></router-view>.

Với ví dụ trên, chúng ta thêm vào URL một chuỗi số duy nhất, ví dụ xxx.com/article/list?t=1496832345025 để bảo đảm URL sẽ khác URL hiện tại khi click vào sidebar.

Một cách khác nữa là chúng ta so sánh route hiện tại với route được click, nếu giống nhau thì chúng ta sử dụng redirect để nhảy tới cùng trang.

Ví dụ

Click vào nút đổi cỡ chữ trên thanh công cụ, bạn sẽ thấy nguyên app-main được reload lại. Nó sử dụng phương pháp redirecting nói trên.

Ví dụ redirect trang đến /redirect khi click chuột

const { fullPath } = this.$route
this.$nextTick(() => {
  this.$router.replace({
    path: '/redirect' + fullPath,
  });
});

Trang redirect được chuyển ngược lại trang trước đó.

// redirect.vue
// https://github.com/tuandm/laravue/blob/master/resources/js/views/redirect/index.vue
export default {
  beforeCreate() {
    const { params, query } = this.$route;
    const { path } = params;
    this.$router.replace({ path: '/' + path, query });
  },
  render: function(h) {
    return h(); // avoid warning message
  },
};

Breadcrumbs là các thẻ (label)/link điều hướng tập hợp nhiều liên kết phân cấp, giúp cho người dùng biết được mình đang ở vị trí nào (theo phân cấp) trong trang web. Ví dụ:

Trang chủ > Chuyên Mục (hoặc thẻ Tag) > Trang con
Quản trị > Quản lý người dùng > Thêm người dùng

Breadcrumbs trong laravue được sinh tự động bằng cách theo dõi thay đổi của $route. tương tự như menu, bạn cũng có thể cấu hình breadcrumb trong routing. Ví dụ: bạn có thể khai báo breadcrumb:false trong route để route đó không hiển thị ở breadcrumbs.

Source code: @/components/Breadcrumb

Liên kết ngoài

Bạn có thể thêm vào các liên kết ngoài (external links) ở sidebar, đơn giản là khai báo đường dẫn của liên kết vào path. Khi người dùng click vào liên kết hợp lệ, browser sẽ mở liên kết đó.

Ví dụ

{
  path: 'external-link',
  component: Layout,
  children: [
    {
      path: 'https://github.com/tuandm/laravue',
      meta: { title: 'externalLink', icon: 'link' },
    },
  ],
},