mirror of
https://gitlab.com/bramw/baserow.git
synced 2024-11-24 16:36:46 +00:00
219 lines
6.2 KiB
Vue
219 lines
6.2 KiB
Vue
<template>
|
|
<div>
|
|
<FieldFormulaInitialSubForm
|
|
:default-values="mergedTypeOptions"
|
|
:formula="values.formula"
|
|
:error="formulaError"
|
|
:formula-type="localOrServerFormulaType"
|
|
:table="table"
|
|
:view="view"
|
|
:primary="primary"
|
|
:loading="refreshingFormula"
|
|
:formula-type-refresh-needed="formulaTypeRefreshNeeded"
|
|
:all-fields-in-table="allFieldsInTable"
|
|
:database="database"
|
|
@open-advanced-context="
|
|
$refs.advancedFormulaEditContext.openContext($event)
|
|
"
|
|
@refresh-formula-type="refreshFormulaType"
|
|
@update-formula="values.formula = $event"
|
|
>
|
|
</FieldFormulaInitialSubForm>
|
|
<FormulaAdvancedEditContext
|
|
ref="advancedFormulaEditContext"
|
|
v-model="values.formula"
|
|
:table="table"
|
|
:fields="fieldsUsableInFormula"
|
|
:error="formulaError"
|
|
:database="database"
|
|
@blur="$v.values.formula.$touch()"
|
|
@hidden="$v.values.formula.$touch()"
|
|
>
|
|
</FormulaAdvancedEditContext>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { required } from 'vuelidate/lib/validators'
|
|
|
|
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/core/formula/parser/parser'
|
|
|
|
export default {
|
|
name: 'FieldFormulaSubForm',
|
|
components: {
|
|
FieldFormulaInitialSubForm,
|
|
FormulaAdvancedEditContext,
|
|
},
|
|
mixins: [form, fieldSubForm],
|
|
props: {
|
|
name: {
|
|
required: true,
|
|
type: String,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
allowedValues: ['formula'],
|
|
values: {
|
|
formula: '',
|
|
},
|
|
fetchedTypeOptions: { error: null },
|
|
mergedTypeOptions: { ...this.defaultValues },
|
|
parsingError: null,
|
|
previousValidParsedFormula: this.defaultValues.formula,
|
|
formulaTypeRefreshNeeded: false,
|
|
refreshingFormula: false,
|
|
}
|
|
},
|
|
computed: {
|
|
localOrServerFormulaType() {
|
|
return (
|
|
this.mergedTypeOptions.array_formula_type ||
|
|
this.mergedTypeOptions.formula_type
|
|
)
|
|
},
|
|
fieldsUsableInFormula() {
|
|
return this.allFieldsInTable.filter((f) => {
|
|
const isNotThisField = f.id !== this.defaultValues.id
|
|
const canBeReferencedByFormulaField = this.$registry
|
|
.get('field', f.type)
|
|
.canBeReferencedByFormulaField(f)
|
|
return isNotThisField && canBeReferencedByFormulaField
|
|
})
|
|
},
|
|
formulaError() {
|
|
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.mergedTypeOptions.error) {
|
|
return this.mergedTypeOptions.error
|
|
} else {
|
|
return null
|
|
}
|
|
},
|
|
},
|
|
watch: {
|
|
defaultValues: {
|
|
deep: true,
|
|
handler(newValue) {
|
|
this.mergedTypeOptions = { ...newValue, ...this.fetchedTypeOptions }
|
|
},
|
|
},
|
|
fetchedTypeOptions: {
|
|
deep: true,
|
|
handler(newValue) {
|
|
this.mergedTypeOptions = { ...this.defaultValues, ...newValue }
|
|
},
|
|
},
|
|
},
|
|
methods: {
|
|
parseFormula(value) {
|
|
if (value == null) {
|
|
return false
|
|
}
|
|
if (!value.trim()) {
|
|
return false
|
|
}
|
|
try {
|
|
parseBaserowFormula(value)
|
|
this.parsingError = null
|
|
if (this.previousValidParsedFormula !== value) {
|
|
this.formulaTypeRefreshNeeded = true
|
|
this.previousValidParsedFormula = value
|
|
}
|
|
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.fetchedTypeOptions.error = error.handler.detail
|
|
this.formulaTypeRefreshNeeded = false
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
},
|
|
reset() {
|
|
this.fetchedTypeOptions = { error: null }
|
|
this.formulaTypeRefreshNeeded = false
|
|
Object.assign(this.mergedTypeOptions, this.defaultValues)
|
|
form.methods.reset.call(this)
|
|
},
|
|
async refreshFormulaType() {
|
|
if (!this.name) {
|
|
this.$emit('validate')
|
|
return
|
|
}
|
|
try {
|
|
this.refreshingFormula = true
|
|
const { data } = await FormulaService(this.$client).type(
|
|
this.table.id,
|
|
this.name,
|
|
this.values.formula
|
|
)
|
|
|
|
this.fetchedTypeOptions = data
|
|
this.formulaTypeRefreshNeeded = false
|
|
} catch (e) {
|
|
if (!this.handleErrorByForm(e)) {
|
|
notifyIf(e, 'field')
|
|
} else {
|
|
this.formulaTypeRefreshNeeded = false
|
|
}
|
|
}
|
|
this.refreshingFormula = false
|
|
},
|
|
},
|
|
validations() {
|
|
return {
|
|
values: {
|
|
formula: {
|
|
required,
|
|
parseFormula: this.parseFormula,
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
</script>
|