Ra mắt hai series mới cực hot Trí tuệ nhân tạo A đến ZPython công cụ không thể thiếu khi nghiên cứu Data science, Machine learning.

Khác biệt trong xử lý dữ liệu form với Vuex

Trong Vue.js xử lý dữ liệu form bằng cách sử dụng v-model là rất đơn giản và thuận tiện, tuy nhiên khi ứng dụng sử dụng VueX với dữ liệu được đưa vào store, chúng ta không thể sử dụng được v-model nữa do sẽ gặp lỗi "Error: [vuex] Do not mutate vuex store state outside mutation handlers". Lỗi này đại loại là thực hiện thay đổi dữ liệu store ngoài phạm vi quản lý của mutation (Xem thêm Vuex là gì để biết thêm về mutation). Lỗi này là rất hay gặp với những người mới làm quen với Vue.js và Vuex, bạn có thể gặp các vấn đề khi sử dụng v-model trong Vuex trên diễn đàn Vue.js hay stackoverflow.

Tại sao không gán dữ liệu hai chiều bằng v-model với dữ liệu store trong Vuex?

Chúng ta cùng xem lại ví dụ sử dụng v-model trong Vue.js để gán dữ liệu hai chiều với dữ liệu được lưu trữ cục bộ:

Ví dụ 1

Sử dụng v-model trong xử lý form với Vuejs

Trong ví dụ trên danh sách người dùng được lưu cục bộ tại component, ví dụ này hoạt động hoàn toàn bình thường. Vấn đề mới nảy sinh khi chúng ta sử dụng Vuex để lưu dữ liệu tập trung trong store Vuex, chúng ta cùng xem xét ví dụ tiếp theo được chỉnh sửa từ ví dụ trên. Về kết quả hai ví dụ này cho kết quả hoàn toàn giống nhau.

Ví dụ 2

mô hình sử dụng v-model với store Vuex gặp lỗi

Tuy nhiên, khi sử dụng Vuex ở chế độ strict:

const store = new Vuex.Store({
  // ...
  strict: true
})

Nếu mở công cụ console lên chúng ta sẽ thấy khi chỉnh sửa thông tin người dùng sẽ có lỗi như sau:

vue@2.3.4:529 Error: [vuex] Do not mutate vuex store state outside mutation handlers.
    at assert (vuex@2.3.1:102)
    at Vue$3.store._vm.$watch.deep (vuex@2.3.1:643)
    at Watcher.run (vue@2.3.4:2864)
    at Watcher.update (vue@2.3.4:2838)
    at Dep.notify (vue@2.3.4:738)
    at Object.reactiveSetter [as name] (vue@2.3.4:968)
    at input (eval at makeFunction (vue@2.3.4:9324), <anonymous>:2:3017)
    at HTMLInputElement.invoker (vue@2.3.4:1743)

Tại sao vậy, quay trở lại cách thức hoạt động của Vuex, Vuex được thiết kế theo kiểu luồng dữ liệu một chiều, toàn bộ các thay đổi dữ liệu trong store phải được thông qua các mutations, các thay đổi bên ngoài quản lý của mutation sẽ báo lỗi, cụ thể là lỗi như ở trên. Vậy khi xử lý dữ liệu form làm việc với store Vuex chúng ta phải làm cách nào, chúng ta sẽ cùng bàn luận các giải pháp trong phần tiếp theo. ## Xử lý dữ liệu form với store Vuex

Giải pháp 1: Gán dữ liệu một chiều qua v-bind và thực hiện cập nhật bằng các sự kiện như input, change

Vuex khuyến cáo nên quản lý form nhập liệu theo cách này, chúng ta cùng xem với ví dụ trên thì cách thức triển khai code như thế nào nhé. Chú ý, trong ví dụ 2 chúng ta chỉ gặp lỗi trong phần chỉnh sửa người dùng do đó chúng ta sẽ chỉ tập trung vào các code liên quan. Textbox chứa Họ và tên của user cần chỉnh sửa như sau:

<input type="text" class="form-control" v-model="user.name" placeholder="Họ và tên">

sẽ được thay thế bằng

<input type="text" class="form-control" v-bind:value="userObjFromStore.name" @input="updateUserObj('name',$event)" placeholder="Họ và tên">

Sử dụng v-bind trong thao tác dữ liệu form với store Vuex

Với v-bind gán dữ liệu một chiều (read only), sau đó khi dữ liệu thay đổi chúng ta sử dụng phương thức updateUserObj để gọi đến các mutation chỉnh sửa dữ liệu trong store.

data () {
  return {
    userObj: {},
    userObjFromStore: {}
  }
},
computed: {
  users() {
    return this.$store.state.users
  }
},
methods: {
  showUpdateUserModal(index) {
    this.userObjFromStore = this.users[index]
  },
  updateUserObj(attribute, e) {
    this.userObj[attribute] = e.target.value
  },
  updateUser() {
    var finalUserObj = Object.assign({}, this.userObjFromStore, this.userObj)
    this.$store.commit('updateUser', this.finalUserObj)
  }
}

Và trong mutation chúng ta thực hiện thay đổi dữ liệu store:

mutations: {
  updateUser: function (state, user, index) {
    state.users.splice(index, 1, user)
  }
}

Code hoàn chỉnh của ví dụ này như sau:

Ví dụ 3

Với cách viết code phải sử dụng v-bind khá là dài dòng, tuy nhiên Vuex được thiết kế bắt buộc dữ liệu phải được thay đổi thông qua mutation tránh các lỗi không kiểm soát khi ứng dụng lớn có hàng trăm hàng nghìn các component với các dữ liệu chồng chéo giống như lỗi hiển thị message ảo đã từng xảy ra với Facebook.

Giải pháp 2: Sử dụng setter trong thuộc tính computed

Với cách sử dụng trên là khá dài dòng hơn v-model, tuy nhiên chúng ta có thể có một giải pháp khác là thuộc tính computed dữ liệu hai chiều sử dụng setter:

<input v-model="name">
// ...
computed: {
  name: {
    get () {
      return this.$store.state.name
    },
    set (value) {
      this.$store.commit('updateName', value)
    }
  }
}

Giải pháp này viết cũng khá tiện, tuy nhiên chỉ có thể thực hiện với các giá trị đơn lẻ, không thực hiện được khi cần cập nhật cả một đối tượng. #### Giải pháp 3: Sử dụng cloneDeep trong lodash để clone đối tượng lấy từ Store Vuex

Giải pháp này do bạn Hiệp gợi ý, rất cám ơn Hiệp về gợi ý này, chúng ta cùng bắt đầu nào. Đầu tiên xin được giới thiệu lodash một thư viện javascript mở rộng của underscore với rất nhiều các tính năng hay và hoạt động cực hiệu quả. Lodash cũng được Laravel giới thiệu trong ứng dụng mẫu đi kèm với Vue.js. Với Lodash chúng ta có thể thực hiện clone một object dễ dàng.

Ví dụ 4

Sử dụng v-model với clone object thao tác dữ liệu form trong Vuex

Trong bài viết Two way data binding and state management with Vuex and strict mode của Hiệp gợi ý, sử dụng _.debounce để cứ 500ms là thực hiện cập nhật vào store Vuex, đây là một cách rất hay, tuy nhiên với ứng dụng quản lý người dùng như trên, việc cập nhật liên tục là không cần thiết, do đó mình chỉ gọi đến mutation khi đã chỉnh sửa xong toàn bộ thông tin. Với các ứng dụng giao tiếp thời gian thực (real time) bạn có thể tham khảo thêm. Giải pháp sử dụng đối tượng được nhân bản này giúp cho code khá gọn gàng và đẹp, một lần nữa cám ơn bạn Hiệp.

Lời kết

Sau khi đào sâu vào thực hiện các ví dụ, chúng ta thấy viết code trong Vue.js thật sự rất đơn giản và tường minh và đó cũng là lý do tại sao Vue.js đang là framework phát triển mạnh mẽ trong thời gian qua. Vuex là một thư viện tuyệt vời kết hợp với các thư viện javascript khác bạn có thể thực hiện viết mã các ứng dụng nhanh chóng với hiệu năng cao. Các bạn có góp ý gì comment dưới bài mình cùng trao đổi nhé.


CÁC BÀI VIẾT KHÁC

FirebirD

Đam mê Toán học, Lập trình. Sở thích chia sẻ kiến thức, Phim hài, Bóng đá, Cà phê sáng với bạn bè.

Lập trình hướng đối tượng trong PHP phần 4: Quản lý lỗi với Exception

Decorator Design Pattern trong PHP

8 Bình luận trong "Khác biệt trong xử lý dữ liệu form với Vuex"

  1. Phan Quân

    2 years ago

    Phản hồi
    Cho mình hỏi nếu update cả một đối tượng thì xử lý như thế nào? Ví dụ, mình có một form chỉnh sửa thông tin người dùng như sau: <form> <label>Tên người dùng:</label> <input type="text" :value="user.name" @input="updateName"> <label>Email:</label> <input type="text" :value="user.email" @input="updateEmail"> <label>Địa chỉ:</label> <input type="text" :value="user.address" @input="updateAddress"> <button type="submit">Cập nhật</button> </form> như vậy phải tách ra nhiều thành nhiều sự kiện input, bạn nào có ví dụ cụ thể giúp mình với?
    1. FirebirD

      2 years ago

      Phản hồi
      Mình đã cập nhật lại các ví dụ, Quân xem các ví dụ trong bài nhé
  2. Hiep

    2 years ago

    Phản hồi
    https://ypereirareis.github.io/blog/2017/04/25/vuejs-two-way-data-binding-state-management-vuex-strict-mode/
    1. FirebirD

      2 years ago

      Phản hồi
      cloneDeep trong lodash là giải pháp rất hay cho việc sử dụng gán dữ liệu hai chiều bằng v-model với dữ liệu trong store VueX, tuy nó tiêu tốn tài nguyên nhiều hơn (mình sẽ nói rõ hơn trong bài) nhưng bù lại chúng ta có thể viết code nhanh, gọn, đẹp... Cám ơn Hiệp đã cho một gợi ý hay, mình sẽ cập nhật các phương án xử lý trong bài viết.
      1. Kulit

        2 years ago

        Phản hồi
        Ví dụ rất thực tế, thanks admin
  3. Hiep

    2 years ago

    Phản hồi
    Mình thấy ad không sử dụng mapStates, mapActions..., mình thấy code nhìn gọn và dễ đọc hơn.
    1. FirebirD

      2 years ago

      Phản hồi
      Thanks Hiệp, do thói quen, hic... mình sẽ sử dụng mapXXX trong các bài viết tới.
  4. Hai

    1 year ago

    Phản hồi
    cho mình hỏi là mình bị báo warning biến $ của jquery thì config như thế nào

Thêm bình luận