Add changes detection to record editing
This commit is contained in:
@@ -13,6 +13,7 @@ export default {
|
||||
processingUndelete: false,
|
||||
processingSubmit: false,
|
||||
record: undefined,
|
||||
initialRecordState: undefined,
|
||||
errors: new validator.Validated(),
|
||||
}
|
||||
},
|
||||
@@ -163,6 +164,8 @@ export default {
|
||||
} else {
|
||||
this.inCreating = false
|
||||
this.inEditing = false
|
||||
// reset the record initial state in cases where the record edit page is redirected to the record view page
|
||||
this.initialRecordState = this.record.clone()
|
||||
|
||||
if (this.showRecordModal) {
|
||||
this.$emit('handle-record-redirect', { recordID: record.recordID, recordPageID: this.page.pageID })
|
||||
@@ -172,6 +175,9 @@ export default {
|
||||
|
||||
if (!isNew) {
|
||||
this.record = record
|
||||
// reset the record initial state in cases where the record edit page is opened on a modal
|
||||
this.initialRecordState = this.record.clone()
|
||||
|
||||
this.determineLayout().then(() => {
|
||||
this.$root.$emit(`refetch-non-record-blocks:${this.page.pageID}`)
|
||||
})
|
||||
@@ -230,6 +236,7 @@ export default {
|
||||
this.inCreating = false
|
||||
this.inEditing = false
|
||||
this.record = record
|
||||
this.initialRecordState = this.record.clone()
|
||||
|
||||
this.$router.push({ name: route, params: { ...this.$route.params, recordID: record.recordID } })
|
||||
}
|
||||
@@ -326,6 +333,7 @@ export default {
|
||||
this.onModalHide()
|
||||
this.fields = []
|
||||
this.record = new compose.Record(this.module, {})
|
||||
this.initialRecordState = this.record.clone()
|
||||
this.$emit('save')
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:record.deleteBulkRecordUpdateFailed')))
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<portal to="topbar-title">
|
||||
{{ title }}
|
||||
</portal>
|
||||
|
||||
<portal to="topbar-tools">
|
||||
<b-button-group
|
||||
v-if="isEdit"
|
||||
@@ -567,6 +566,10 @@ export default {
|
||||
return this.$route.name === 'admin.modules.edit' ? this.$t('edit.edit') : this.$t('edit.create')
|
||||
},
|
||||
|
||||
isNew () {
|
||||
return this.moduleID === NoID
|
||||
},
|
||||
|
||||
trModule: {
|
||||
get () {
|
||||
if (!this.module) {
|
||||
@@ -708,11 +711,16 @@ export default {
|
||||
}),
|
||||
|
||||
checkUnsavedModule (next) {
|
||||
if (!this.module.deletedAt) {
|
||||
return next(!isEqual(this.module.clone(), this.initialModuleState.clone()) ? window.confirm(this.$t('general.unsavedChanges')) : true)
|
||||
}
|
||||
if (this.isNew) {
|
||||
next(true)
|
||||
} else if (!this.module.deletedAt) {
|
||||
const moduleState = this.module ? this.module.clone() : {}
|
||||
const initialModuleState = this.initialModuleState ? this.initialModuleState.clone() : {}
|
||||
|
||||
next()
|
||||
next(!isEqual(moduleState, initialModuleState) ? window.confirm(this.$t('general.unsavedChanges')) : true)
|
||||
} else {
|
||||
next(true)
|
||||
}
|
||||
},
|
||||
|
||||
handleNewField () {
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { isEqual } from 'lodash'
|
||||
import { mapGetters } from 'vuex'
|
||||
import RecordToolbar from 'corteza-webapp-compose/src/components/Common/RecordToolbar'
|
||||
import record from 'corteza-webapp-compose/src/mixins/record'
|
||||
@@ -156,6 +157,10 @@ export default {
|
||||
getNextAndPrevRecord: 'ui/getNextAndPrevRecord',
|
||||
}),
|
||||
|
||||
isNew () {
|
||||
return this.record.recordID === NoID
|
||||
},
|
||||
|
||||
title () {
|
||||
const { name, handle } = this.module
|
||||
const titlePrefix = this.inCreating ? 'create' : this.inEditing ? 'edit' : 'view'
|
||||
@@ -232,6 +237,7 @@ export default {
|
||||
created () {
|
||||
this.createBlocks()
|
||||
this.record = new compose.Record(this.module, { values: this.values })
|
||||
this.initialRecordState = this.record.clone()
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
@@ -239,6 +245,14 @@ export default {
|
||||
this.setDefaultValues()
|
||||
},
|
||||
|
||||
beforeRouteLeave (to, from, next) {
|
||||
this.checkUnsavedChanges(next, to)
|
||||
},
|
||||
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
this.checkUnsavedChanges(next, to)
|
||||
},
|
||||
|
||||
methods: {
|
||||
createBlocks () {
|
||||
this.fields.forEach(f => {
|
||||
@@ -267,6 +281,7 @@ export default {
|
||||
response()
|
||||
.then(record => {
|
||||
this.record = new compose.Record(module, record)
|
||||
this.initialRecordState = this.record.clone()
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!axios.isCancel(e)) {
|
||||
@@ -322,6 +337,17 @@ export default {
|
||||
cancel()
|
||||
})
|
||||
},
|
||||
|
||||
checkUnsavedChanges (next, to) {
|
||||
if (this.isNew) {
|
||||
return true
|
||||
} else {
|
||||
const recordValues = JSON.parse(JSON.stringify(this.record.values))
|
||||
const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState.values))
|
||||
|
||||
next(!isEqual(recordValues, initialRecordState) ? window.confirm(this.$t('general:editor.unsavedChanges')) : true)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -782,15 +782,17 @@ export default {
|
||||
},
|
||||
|
||||
checkUnsavedNamespace (next) {
|
||||
if (!this.namespace.deletedAt) {
|
||||
if (this.isNew) {
|
||||
next(true)
|
||||
} else if (!this.namespace.deletedAt) {
|
||||
const namespaceState = !isEqual(this.namespace.clone(), this.initialNamespaceState.clone())
|
||||
const isApplicationState = !(this.isApplication === this.isApplicationInitialState)
|
||||
const namespaceAssetsState = !isEqual(this.namespaceAssets, this.namespaceAssetsInitialState)
|
||||
|
||||
return next((namespaceState || isApplicationState || namespaceAssetsState) ? window.confirm(this.$t('manage.unsavedChanges')) : true)
|
||||
} else {
|
||||
next(true)
|
||||
}
|
||||
|
||||
next()
|
||||
},
|
||||
|
||||
setDefaultValues () {
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isEqual } from 'lodash'
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Grid from 'corteza-webapp-compose/src/components/Public/Page/Grid'
|
||||
import RecordToolbar from 'corteza-webapp-compose/src/components/Common/RecordToolbar'
|
||||
@@ -295,7 +296,11 @@ export default {
|
||||
immediate: true,
|
||||
handler () {
|
||||
this.record = undefined
|
||||
this.initialRecordState = undefined
|
||||
this.refresh()
|
||||
this.loadRecord().then(() => {
|
||||
this.determineLayout()
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -339,6 +344,10 @@ export default {
|
||||
|
||||
mounted () {
|
||||
this.$root.$on('refetch-record-blocks', this.refetchRecordBlocks)
|
||||
|
||||
if (this.showRecordModal) {
|
||||
this.$root.$on('bv::modal::hide', this.checkUnsavedChangesOnModal)
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
@@ -347,6 +356,16 @@ export default {
|
||||
this.setDefaultValues()
|
||||
},
|
||||
|
||||
// Destroy event before route leave to ensure it doesn't destroy the newly created one
|
||||
beforeRouteLeave (to, from, next) {
|
||||
this.$root.$off('refetch-record-blocks', this.refetchRecordBlocks)
|
||||
this.checkUnsavedChanges(next, to)
|
||||
},
|
||||
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
this.checkUnsavedChanges(next, to)
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions({
|
||||
popPreviousPages: 'ui/popPreviousPages',
|
||||
@@ -374,6 +393,7 @@ export default {
|
||||
.then(record => {
|
||||
return new Promise(resolve => setTimeout(resolve, 300)).then(() => {
|
||||
this.record = new compose.Record(module, record)
|
||||
this.initialRecordState = this.record.clone()
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
@@ -384,6 +404,7 @@ export default {
|
||||
})
|
||||
} else {
|
||||
this.record = new compose.Record(module, { values: this.values })
|
||||
this.initialRecordState = this.record.clone()
|
||||
|
||||
this.inEditing = true
|
||||
this.inCreating = true
|
||||
@@ -431,6 +452,7 @@ export default {
|
||||
this.inEditing = true
|
||||
this.inCreating = true
|
||||
this.record = new compose.Record(this.module, { values: this.values })
|
||||
this.initialRecordState = this.record.clone()
|
||||
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID })
|
||||
},
|
||||
|
||||
@@ -442,6 +464,8 @@ export default {
|
||||
|
||||
this.inEditing = true
|
||||
this.inCreating = true
|
||||
this.record = new compose.Record(this.module, { values: this.record.values })
|
||||
this.initialRecordState = this.record.clone()
|
||||
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID, values: this.record.values })
|
||||
},
|
||||
|
||||
@@ -607,6 +631,35 @@ export default {
|
||||
|
||||
destroyEvents () {
|
||||
this.$root.$off('refetch-record-blocks', this.refetchRecordBlocks)
|
||||
|
||||
if (this.showRecordModal) {
|
||||
this.$root.$off('bv::modal::hide', this.checkUnsavedChangesOnModal)
|
||||
}
|
||||
},
|
||||
|
||||
compareRecordValues () {
|
||||
const recordValues = JSON.parse(JSON.stringify(this.record ? this.record.values : {}))
|
||||
const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState ? this.initialRecordState.values : {}))
|
||||
|
||||
return !isEqual(recordValues, initialRecordState)
|
||||
},
|
||||
|
||||
checkUnsavedChanges (next, to) {
|
||||
if (this.inCreating) {
|
||||
next(true)
|
||||
} else {
|
||||
next(this.compareRecordValues() ? window.confirm(this.$t('general:editor.unsavedChanges')) : true)
|
||||
}
|
||||
},
|
||||
|
||||
checkUnsavedChangesOnModal (bvEvent, modalId) {
|
||||
if (modalId === 'record-modal' && !this.inCreating) {
|
||||
const recordStateChange = this.compareRecordValues() ? window.confirm(this.$t('general:editor.unsavedChanges')) : true
|
||||
|
||||
if (!recordStateChange) {
|
||||
bvEvent.preventDefault()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -174,4 +174,6 @@ variants:
|
||||
info: Info
|
||||
success: Success
|
||||
danger: Danger
|
||||
warning: Warning
|
||||
warning: Warning
|
||||
editor:
|
||||
unsavedChanges: Unsaved changes will be lost. Do you wish to leave the page?
|
||||
|
||||
Reference in New Issue
Block a user