Computed 属性 setter 未被调用
Computed property setter is not being invoked
所以我有一个计算的 属性,它是我在 Vuex 商店中定义的一种类型的对象。我正在尝试在我的组件上使用这个 属性 和 v-model。我已经为这个 属性 定义了 getter 和 setters 并且我很确定我已经正确配置了所有内容,除非我忽略了什么。我尝试在 setter 中进行控制台登录以查看它是否曾经启动,但控制台中没有任何记录,因此根本没有被调用。任何帮助将不胜感激
商店代码:
import { Module, VuexModule, getModule, Mutation, Action } from 'vuex-module-decorators'
import { websocket } from 'src/boot/socket.io-client'
import store from 'src/store'
import { DataTablePagination } from '../types'
import { Course } from './types'
import { MessageModule } from 'src/store/message/index'
import { Notify } from 'quasar'
import { validate } from "class-validator"
export { Course } from './types'
export { DataTablePagination } from '../types'
export interface CourseState {
pagination: DataTablePagination
courses: Course []
filter: string,
disabled: boolean,
selected: Course [],
active: boolean,
editCourseOpened: boolean,
addCourseOpened: boolean,
addCourseData: Course,
editCourseData: Course,
addCourseDisabled: boolean
}
@Module({
name: 'course',
namespaced: true,
dynamic: true,
store
})
class CourseClass extends VuexModule implements CourseState {
public pagination: DataTablePagination = {
descending: false,
rowsNumber: 0,
rowsPerPage: 10,
page: 1,
sortBy: 'name'
}
public courses: Course [] = []
public filter = ''
public disabled = true
public selected: Course [] = []
public active = true
public editCourseOpened = false
public addCourseOpened = false
public addCourseData = new Course()
public editCourseData = new Course()
public addCourseDisabled = true
@Mutation
SET_ADDCOURSEDISABLED(disabled: boolean) {
this.addCourseDisabled=disabled
}
@Mutation
SET_EDITCOURSEDATA(data: Course) {
this.editCourseData.copy(data)
}
// public SET_ADDCOURSEDATA(payload: { key: string, value: number | string }) {
// const { key, value } = payload
// if (Object.prototype.hasOwnProperty.call(this, key)) {
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
// (this as any)[key] = value
// }
// }
@Mutation
SET_ADDCOURSEDATA(data: Course) {
this.addCourseData.copy(data)
}
@Mutation
SET_EDITCOURSEOPENED(opened: boolean) {
this.editCourseOpened=opened
}
@Mutation
SET_ADDCOURSEOPENED(opened: boolean) {
this.addCourseOpened=opened
}
@Mutation
SET_ACTIVE(active: boolean) {
this.active=active
}
@Mutation
SET_PAGINATION(pagination: DataTablePagination) {
this.pagination=pagination
}
@Mutation
SET_SELECTED(selected: Course []) {
this.selected=selected
}
@Mutation
SET_FILTER(filter: string) {
this.filter=filter
}
@Mutation
SET_COURSES(courses: Course []) {
this.courses=courses
}
@Mutation
SET_DISABLED(disabled: boolean) {
this.disabled=disabled
}
@Action
public ValidateAddCourseData() {
validate(this.addCourseDisabled).then(errors => {
console.log(errors)
if(errors.length) {
this.SET_ADDCOURSEDISABLED(true)
} else {
this.SET_ADDCOURSEDISABLED(false)
}
})
}
@Action
public async addCourse(input: Course) {
websocket.emit('query', `mutation {
createCourse (
course: {
code: "${input.code}"
name: "${input.name}"
creditHours: ${input.creditHours}
numberOfLabs: ${input.numberOfLabs}
contactHours: ${input.contactHours}
chargeableCredits: 0
}
) {
ok
message
}
}`, (response: {
errors: any
data: {
createAcademicProgram: {
ok: boolean
message: String
}
}
}) => {
if(response.data) {
this.fetchCourses()
Notify.create({
timeout: 3000,
position: 'center',
color: 'primary',
message: 'Course Added Successfully'
})
this.SET_ADDCOURSEOPENED(!this.addCourseOpened)
}
else {
MessageModule.newMessage({title: 'Error: Addition Failed', icon: 'error', message: 'Addition failed. Ensure that the course code you have entered is unique or contact your system administrator'})
}
})
}
@Action
public async deleteCourse(input: Course) {
websocket.emit('query', `mutation {
deleteCourse (
courseId: "${input.id}"
) {
id
ok
message
}
}`, (response: {
errors: any;
data: {
deleteCourse: {
id: any;
ok: boolean;
message: String
}
}
}) => {
if(response.data) {
this.fetchCourses()
Notify.create({
timeout: 2000,
position: 'center',
color: 'primary',
message: 'Course Deleted Successfully'
})
}
else {
MessageModule.newMessage({ title: 'Deletion Failed', icon: 'error', message: 'Deletion Failed. Ensure that no course instances are still tied to this course or contact your system administrator' })
}
})
}
@Action
public async editCourse(input: Course) {
websocket.emit('query', `mutation {
updateCourse (
course: {
id: "${input.id}"
code: "${input.code}"
name: "${input.name}"
creditHours: ${input.creditHours}
numberOfLabs: ${input.numberOfLabs}
contactHours: ${input.contactHours}
chargeableCredits: 0
}
) {
id
ok
message
}
}`, (response: {
errors: any
data: {
updateCourse: {
id: string
ok: boolean
message: string
}
}
}) => {
if(response.data) {
this.fetchCourses()
this.SET_SELECTED([input])
this.SET_EDITCOURSEOPENED(!this.editCourseOpened)
Notify.create({
timeout: 3000,
color: 'primary',
message: 'Course Updated Successfully',
position: 'center'
})
}
else {
MessageModule.newMessage({ title: 'Update Failed', icon: 'error', message: 'Update Failed. Ensure that all course codes are unique or contact your system administrator'})
}
})
}
@Action
public async fetchCourses() {
const order = this.pagination.sortBy !== null ? `order: {
by: ${this.pagination.sortBy}
dir: ${this.pagination.descending ? 'DESC' : 'ASC'}
}` : ''
websocket.emit('query', `{
courses(
page: {
skip: ${(this.pagination.page - 1) * this.pagination.rowsPerPage},
first: ${this.pagination.rowsPerPage}
}
filter: {
ilike: {
name: "${this.filter}"
}
}
${order}
) {
pagination {
total
listTotal
}
list {
id
code
name
creditHours
numberOfLabs
contactHours
}
}
}`, (response: {
errors: any
data: {
courses: {
list: Course[]
pagination: {
total: number
listTotal: number
}
}
}
}) => {
this.SET_COURSES(response.data.courses.list)
this.pagination.rowsNumber = response.data.courses.pagination.total
})
}
}
export const CourseModule = getModule(CourseClass)
组件代码:
<template>
<q-dialog v-model="isOpened" :bordered="true">
<q-card style="width: 50vw;">
<q-toolbar class="bg-grey-5 text-center">
<q-toolbar-title>Add Course</q-toolbar-title>
<q-btn flat round dense icon="close" v-close-popup />
</q-toolbar>
<q-card-section class="col">
<q-input v-model.trim="course.name" label="Name" type="object"
:rules="[
val => val.length > 0 || 'Required field'
]"/>
<q-input v-model="course.code" label="Code"
:rules="[
val => val.length > 0 || 'Required field'
]"/>
<q-input v-model.number="course.creditHours" label="Credit Hours" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-input v-model.number="course.numberOfLabs" label="Number of Labs" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-input v-model.number="course.contactHours" label="Contact Hours" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-card-actions align="right">
<q-btn color="primary" :disable="disabled" @click="submit();">
<q-icon name="save" />
<q-tooltip
anchor="top middle"
self="bottom middle"
:offset="[10, 10]"
>
<strong>Add Course</strong>
</q-tooltip>
</q-btn>
<q-btn color="red" @click="toggle()" icon="cancel">
<q-tooltip
anchor="top middle"
self="bottom middle"
:offset="[10, 10]"
>
<strong>Cancel</strong>
</q-tooltip>
</q-btn>
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Vue, Component, Watch } from 'vue-property-decorator'
import { CourseModule, Course } from 'src/store/course/index'
@Component
export default class AddCourse extends Vue {
//private course: Course = new Course()
//private disabled = true
get disabled() {
return CourseModule.addCourseDisabled
}
get course() {
return CourseModule.addCourseData
}
set course(args: any) {
console.log(args)
//CourseModule.SET_ADDCOURSEDATA(newValue)
}
get isOpened() {
return CourseModule.addCourseOpened
}
set isOpened(newValue: boolean) {
CourseModule.SET_ADDCOURSEOPENED(newValue)
}
toggle() {
CourseModule.SET_ADDCOURSEDATA(this.course)
this.isOpened = !this.isOpened
// if(this.isOpened) {
// this.getdisabled()
// }
// else {
// this.course.clear()
// }
}
submit() {
CourseModule.addCourse(this.course)
//this.toggle()
}
// getdisabled() {
// validate(this.course).then(errors => {
// if(errors.length) {
// this.disabled=true
// } else {
// this.disabled=false
// }
// })
// return true
// }
}
</script>
正如您已经注意到的,这里的标准解决方案是为每个表单域计算一个 属性,每个表单域都有 get
和 set
商店。
一个可行的替代方法是使用 Proxy
拦截对象属性的设置。我不知道如何用 TypeScript 写这个,但这不应该影响核心思想:
const store = new Vuex.Store({
state: {
course: {
name: 'Algebra',
teacher: 'Smith'
}
},
mutations: {
course (state, course) {
state.course = course
}
}
})
new Vue({
el: '#app',
store,
computed: {
course () {
const store = this.$store
const course = store.state.course
return new Proxy(course, {
set (obj, key, value) {
store.commit('course', { ...obj, [key]: value })
return true
}
})
}
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>
<div id="app">
<input v-model="course.name">
<br>
<input v-model="course.teacher">
<br>
<p>{{ course.name }}</p>
<p>{{ course.teacher }}</p>
</div>
如果您需要支持 IE,那么您将无法使用 Proxy
,但应该可以使用标准 JavaScript 属性实现类似的功能。这个想法与正确的 Proxy
非常相似,但您必须在 'proxy' 对象上显式创建 get
和 set
处理程序。
所以我有一个计算的 属性,它是我在 Vuex 商店中定义的一种类型的对象。我正在尝试在我的组件上使用这个 属性 和 v-model。我已经为这个 属性 定义了 getter 和 setters 并且我很确定我已经正确配置了所有内容,除非我忽略了什么。我尝试在 setter 中进行控制台登录以查看它是否曾经启动,但控制台中没有任何记录,因此根本没有被调用。任何帮助将不胜感激
商店代码:
import { Module, VuexModule, getModule, Mutation, Action } from 'vuex-module-decorators'
import { websocket } from 'src/boot/socket.io-client'
import store from 'src/store'
import { DataTablePagination } from '../types'
import { Course } from './types'
import { MessageModule } from 'src/store/message/index'
import { Notify } from 'quasar'
import { validate } from "class-validator"
export { Course } from './types'
export { DataTablePagination } from '../types'
export interface CourseState {
pagination: DataTablePagination
courses: Course []
filter: string,
disabled: boolean,
selected: Course [],
active: boolean,
editCourseOpened: boolean,
addCourseOpened: boolean,
addCourseData: Course,
editCourseData: Course,
addCourseDisabled: boolean
}
@Module({
name: 'course',
namespaced: true,
dynamic: true,
store
})
class CourseClass extends VuexModule implements CourseState {
public pagination: DataTablePagination = {
descending: false,
rowsNumber: 0,
rowsPerPage: 10,
page: 1,
sortBy: 'name'
}
public courses: Course [] = []
public filter = ''
public disabled = true
public selected: Course [] = []
public active = true
public editCourseOpened = false
public addCourseOpened = false
public addCourseData = new Course()
public editCourseData = new Course()
public addCourseDisabled = true
@Mutation
SET_ADDCOURSEDISABLED(disabled: boolean) {
this.addCourseDisabled=disabled
}
@Mutation
SET_EDITCOURSEDATA(data: Course) {
this.editCourseData.copy(data)
}
// public SET_ADDCOURSEDATA(payload: { key: string, value: number | string }) {
// const { key, value } = payload
// if (Object.prototype.hasOwnProperty.call(this, key)) {
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
// (this as any)[key] = value
// }
// }
@Mutation
SET_ADDCOURSEDATA(data: Course) {
this.addCourseData.copy(data)
}
@Mutation
SET_EDITCOURSEOPENED(opened: boolean) {
this.editCourseOpened=opened
}
@Mutation
SET_ADDCOURSEOPENED(opened: boolean) {
this.addCourseOpened=opened
}
@Mutation
SET_ACTIVE(active: boolean) {
this.active=active
}
@Mutation
SET_PAGINATION(pagination: DataTablePagination) {
this.pagination=pagination
}
@Mutation
SET_SELECTED(selected: Course []) {
this.selected=selected
}
@Mutation
SET_FILTER(filter: string) {
this.filter=filter
}
@Mutation
SET_COURSES(courses: Course []) {
this.courses=courses
}
@Mutation
SET_DISABLED(disabled: boolean) {
this.disabled=disabled
}
@Action
public ValidateAddCourseData() {
validate(this.addCourseDisabled).then(errors => {
console.log(errors)
if(errors.length) {
this.SET_ADDCOURSEDISABLED(true)
} else {
this.SET_ADDCOURSEDISABLED(false)
}
})
}
@Action
public async addCourse(input: Course) {
websocket.emit('query', `mutation {
createCourse (
course: {
code: "${input.code}"
name: "${input.name}"
creditHours: ${input.creditHours}
numberOfLabs: ${input.numberOfLabs}
contactHours: ${input.contactHours}
chargeableCredits: 0
}
) {
ok
message
}
}`, (response: {
errors: any
data: {
createAcademicProgram: {
ok: boolean
message: String
}
}
}) => {
if(response.data) {
this.fetchCourses()
Notify.create({
timeout: 3000,
position: 'center',
color: 'primary',
message: 'Course Added Successfully'
})
this.SET_ADDCOURSEOPENED(!this.addCourseOpened)
}
else {
MessageModule.newMessage({title: 'Error: Addition Failed', icon: 'error', message: 'Addition failed. Ensure that the course code you have entered is unique or contact your system administrator'})
}
})
}
@Action
public async deleteCourse(input: Course) {
websocket.emit('query', `mutation {
deleteCourse (
courseId: "${input.id}"
) {
id
ok
message
}
}`, (response: {
errors: any;
data: {
deleteCourse: {
id: any;
ok: boolean;
message: String
}
}
}) => {
if(response.data) {
this.fetchCourses()
Notify.create({
timeout: 2000,
position: 'center',
color: 'primary',
message: 'Course Deleted Successfully'
})
}
else {
MessageModule.newMessage({ title: 'Deletion Failed', icon: 'error', message: 'Deletion Failed. Ensure that no course instances are still tied to this course or contact your system administrator' })
}
})
}
@Action
public async editCourse(input: Course) {
websocket.emit('query', `mutation {
updateCourse (
course: {
id: "${input.id}"
code: "${input.code}"
name: "${input.name}"
creditHours: ${input.creditHours}
numberOfLabs: ${input.numberOfLabs}
contactHours: ${input.contactHours}
chargeableCredits: 0
}
) {
id
ok
message
}
}`, (response: {
errors: any
data: {
updateCourse: {
id: string
ok: boolean
message: string
}
}
}) => {
if(response.data) {
this.fetchCourses()
this.SET_SELECTED([input])
this.SET_EDITCOURSEOPENED(!this.editCourseOpened)
Notify.create({
timeout: 3000,
color: 'primary',
message: 'Course Updated Successfully',
position: 'center'
})
}
else {
MessageModule.newMessage({ title: 'Update Failed', icon: 'error', message: 'Update Failed. Ensure that all course codes are unique or contact your system administrator'})
}
})
}
@Action
public async fetchCourses() {
const order = this.pagination.sortBy !== null ? `order: {
by: ${this.pagination.sortBy}
dir: ${this.pagination.descending ? 'DESC' : 'ASC'}
}` : ''
websocket.emit('query', `{
courses(
page: {
skip: ${(this.pagination.page - 1) * this.pagination.rowsPerPage},
first: ${this.pagination.rowsPerPage}
}
filter: {
ilike: {
name: "${this.filter}"
}
}
${order}
) {
pagination {
total
listTotal
}
list {
id
code
name
creditHours
numberOfLabs
contactHours
}
}
}`, (response: {
errors: any
data: {
courses: {
list: Course[]
pagination: {
total: number
listTotal: number
}
}
}
}) => {
this.SET_COURSES(response.data.courses.list)
this.pagination.rowsNumber = response.data.courses.pagination.total
})
}
}
export const CourseModule = getModule(CourseClass)
组件代码:
<template>
<q-dialog v-model="isOpened" :bordered="true">
<q-card style="width: 50vw;">
<q-toolbar class="bg-grey-5 text-center">
<q-toolbar-title>Add Course</q-toolbar-title>
<q-btn flat round dense icon="close" v-close-popup />
</q-toolbar>
<q-card-section class="col">
<q-input v-model.trim="course.name" label="Name" type="object"
:rules="[
val => val.length > 0 || 'Required field'
]"/>
<q-input v-model="course.code" label="Code"
:rules="[
val => val.length > 0 || 'Required field'
]"/>
<q-input v-model.number="course.creditHours" label="Credit Hours" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-input v-model.number="course.numberOfLabs" label="Number of Labs" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-input v-model.number="course.contactHours" label="Contact Hours" type="number"
:rules="[
val => val >= 0 || 'No negatives allowed'
]"/>
<q-card-actions align="right">
<q-btn color="primary" :disable="disabled" @click="submit();">
<q-icon name="save" />
<q-tooltip
anchor="top middle"
self="bottom middle"
:offset="[10, 10]"
>
<strong>Add Course</strong>
</q-tooltip>
</q-btn>
<q-btn color="red" @click="toggle()" icon="cancel">
<q-tooltip
anchor="top middle"
self="bottom middle"
:offset="[10, 10]"
>
<strong>Cancel</strong>
</q-tooltip>
</q-btn>
</q-card-actions>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Vue, Component, Watch } from 'vue-property-decorator'
import { CourseModule, Course } from 'src/store/course/index'
@Component
export default class AddCourse extends Vue {
//private course: Course = new Course()
//private disabled = true
get disabled() {
return CourseModule.addCourseDisabled
}
get course() {
return CourseModule.addCourseData
}
set course(args: any) {
console.log(args)
//CourseModule.SET_ADDCOURSEDATA(newValue)
}
get isOpened() {
return CourseModule.addCourseOpened
}
set isOpened(newValue: boolean) {
CourseModule.SET_ADDCOURSEOPENED(newValue)
}
toggle() {
CourseModule.SET_ADDCOURSEDATA(this.course)
this.isOpened = !this.isOpened
// if(this.isOpened) {
// this.getdisabled()
// }
// else {
// this.course.clear()
// }
}
submit() {
CourseModule.addCourse(this.course)
//this.toggle()
}
// getdisabled() {
// validate(this.course).then(errors => {
// if(errors.length) {
// this.disabled=true
// } else {
// this.disabled=false
// }
// })
// return true
// }
}
</script>
正如您已经注意到的,这里的标准解决方案是为每个表单域计算一个 属性,每个表单域都有 get
和 set
商店。
一个可行的替代方法是使用 Proxy
拦截对象属性的设置。我不知道如何用 TypeScript 写这个,但这不应该影响核心思想:
const store = new Vuex.Store({
state: {
course: {
name: 'Algebra',
teacher: 'Smith'
}
},
mutations: {
course (state, course) {
state.course = course
}
}
})
new Vue({
el: '#app',
store,
computed: {
course () {
const store = this.$store
const course = store.state.course
return new Proxy(course, {
set (obj, key, value) {
store.commit('course', { ...obj, [key]: value })
return true
}
})
}
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>
<div id="app">
<input v-model="course.name">
<br>
<input v-model="course.teacher">
<br>
<p>{{ course.name }}</p>
<p>{{ course.teacher }}</p>
</div>
如果您需要支持 IE,那么您将无法使用 Proxy
,但应该可以使用标准 JavaScript 属性实现类似的功能。这个想法与正确的 Proxy
非常相似,但您必须在 'proxy' 对象上显式创建 get
和 set
处理程序。