1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-12 16:28:06 +00:00
bramw_baserow/web-frontend/modules/database/components/field/FieldFormulaSubForm.vue
2021-11-24 10:53:00 +00:00

231 lines
6.7 KiB
Vue

<template>
<div>
<FieldFormulaInitialSubForm
:default-values="mergedTypeOptions"
:formula="values.formula"
:error="localOrServerError"
:formula-type="localOrServerFormulaType"
:table="table"
:loading="refreshingFormula"
:formula-type-refresh-needed="formulaTypeRefreshNeeded"
@open-advanced-context="
$refs.advancedFormulaEditContext.openContext($event)
"
@refresh-formula-type="refreshFormulaType"
>
</FieldFormulaInitialSubForm>
<FormulaAdvancedEditContext
ref="advancedFormulaEditContext"
v-model="values.formula"
:table="table"
:fields="fieldsUsableInFormula"
:error="localOrServerError"
@blur="$v.values.formula.$touch()"
>
</FormulaAdvancedEditContext>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
import { mapGetters } from 'vuex'
import form from '@baserow/modules/core/mixins/form'
import { notifyIf } from '@baserow/modules/core/utils/error'
import fieldSubForm from '@baserow/modules/database/mixins/fieldSubForm'
import FieldFormulaInitialSubForm from '@baserow/modules/database/components/formula/FieldFormulaInitialSubForm'
import FormulaAdvancedEditContext from '@baserow/modules/database/components/formula/FormulaAdvancedEditContext'
import FormulaService from '@baserow/modules/database/services/formula'
import parseBaserowFormula from '@baserow/modules/database/formula/parser/parser'
export default {
name: 'FieldFormulaSubForm',
components: {
FieldFormulaInitialSubForm,
FormulaAdvancedEditContext,
},
mixins: [form, fieldSubForm],
data() {
return {
allowedValues: ['formula'],
values: {
formula: '',
},
typeOptions: {},
mergedTypeOptions: Object.assign({}, this.defaultValues),
parsingError: null,
errorFromServer: null,
localFormulaType: null,
localArrayFormulaType: null,
initialFormula: null,
refreshingFormula: false,
}
},
computed: {
...mapGetters({
rawFields: 'field/getAllWithPrimary',
}),
localOrServerFormulaType() {
return this.localFormulaType
? this.localArrayFormulaType || this.localFormulaType
: this.defaultValues.array_formula_type ||
this.defaultValues.formula_type
},
fieldsUsableInFormula() {
return this.rawFields.filter((f) => {
const isNotThisField = f.id !== this.defaultValues.id
const canBeReferencedByFormulaField = this.$registry
.get('field', f.type)
.canBeReferencedByFormulaField()
return isNotThisField && canBeReferencedByFormulaField
})
},
localOrServerError() {
const dirty = this.$v.values.formula.$dirty
if (dirty && !this.$v.values.formula.required) {
return 'Please enter a formula'
} else if (dirty && !this.$v.values.formula.parseFormula) {
return (
`Error in the formula on line ${this.parsingError.line} starting at
letter ${this.parsingError.character}` +
'\n' +
this.toHumanReadableErrorMessage(this.parsingError)
)
} else if (this.errorFromServer) {
return this.errorFromServer
} else if (this.defaultValues.error) {
return this.defaultValues.error
} else {
return null
}
},
formulaChanged() {
return (
this.initialFormula !== null &&
this.values.formula !== this.initialFormula
)
},
updatingExistingFormula() {
return !!this.defaultValues.id
},
formulaTypeRefreshNeeded() {
return (
this.formulaChanged &&
!this.parsingError &&
this.updatingExistingFormula
)
},
},
watch: {
defaultValues(newValue, oldValue) {
this.mergedTypeOptions = Object.assign({}, newValue)
},
'values.formula'(newValue, oldValue) {
this.parseFormula(newValue)
},
},
methods: {
parseFormula(value) {
if (value == null) {
return false
}
if (!value.trim()) {
return false
}
try {
parseBaserowFormula(value)
if (!this.initialFormula) {
this.initialFormula = this.values.formula
}
this.parsingError = null
return true
} catch (e) {
this.parsingError = e
return false
}
},
toHumanReadableErrorMessage(error) {
const s = error.message
.replace('extraneous', 'Invalid')
.replace('input', 'letters')
.replace(' expecting', ', was instead expecting ')
.replace("'<EOF>'", 'the end of the formula')
.replace('<EOF>', 'the end of the formula')
.replace('mismatched letters', 'Unexpected')
.replace('Unexpected the', 'Unexpected')
.replace('SINGLEQ_STRING_LITERAL', 'a single quoted string')
.replace('DOUBLEQ_STRING_LITERAL', 'a double quoted string')
.replace('IDENTIFIER', 'a function')
.replace('IDENTIFIER_UNICODE', '')
.replace('{', '')
.replace('}', '')
return s + '.'
},
handleErrorByForm(error) {
if (
[
'ERROR_WITH_FORMULA',
'ERROR_FIELD_SELF_REFERENCE',
'ERROR_FIELD_CIRCULAR_REFERENCE',
].includes(error.handler.code)
) {
this.errorFromServer = error.handler.detail
return true
} else {
return false
}
},
reset() {
const formula = this.values.formula
form.methods.reset.call(this)
this.errorFromServer = null
this.initialFormula = formula
this.values.formula = formula
},
async refreshFormulaType() {
try {
this.refreshingFormula = true
const { data } = await FormulaService(this.$client).type(
this.defaultValues.id,
this.values.formula
)
// eslint-disable-next-line camelcase
const { formula_type, array_formula_type, error, ...otherTypeOptions } =
data
this.mergedTypeOptions = Object.assign(
{},
this.mergedTypeOptions,
otherTypeOptions
)
if (error) {
this.errorFromServer = `Error with formula: ${error}.`
} else {
this.errorFromServer = null
}
// eslint-disable-next-line camelcase
this.localFormulaType = formula_type
// eslint-disable-next-line camelcase
this.localArrayFormulaType = array_formula_type
} catch (e) {
if (!this.handleErrorByForm(e)) {
notifyIf(e, 'field')
}
}
this.initialFormula = this.values.formula
this.refreshingFormula = false
},
},
validations() {
return {
values: {
formula: {
required,
parseFormula: this.parseFormula,
},
},
}
},
}
</script>