mirror of
https://github.com/directus/directus.git
synced 2026-01-28 11:38:11 -05:00
Performance improvements for groups within v-form (#14188)
* added explicit change check before updating the internal value
* bunch of attempted fixes
* Revert "bunch of attempted fixes"
This reverts commit 17b7a5340f.
* nested v-form performance improvements
preventing re-rendering of some groups
* tries to fix accordion always re-rendering
* abnstracted the fix to its own functions
* abstracted the fix to its own functions
* fixes bug with rendering and saving data in accordion groups
* removed unneeded if statement
Co-authored-by: ian <licitdev@gmail.com>
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
import { getJSType } from '@/utils/get-js-type';
|
||||
import { Field, ValidationError } from '@directus/shared/types';
|
||||
import { isEqual } from 'lodash';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FormFieldInterface from './form-field-interface.vue';
|
||||
import FormFieldLabel from './form-field-label.vue';
|
||||
@@ -117,22 +117,7 @@ const isDisabled = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const defaultValue = computed(() => {
|
||||
const value = props.field?.schema?.default_value;
|
||||
|
||||
if (value !== undefined) return value;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const internalValue = computed(() => {
|
||||
if (props.modelValue !== undefined) return props.modelValue;
|
||||
if (props.initialValue !== undefined) return props.initialValue;
|
||||
return defaultValue.value;
|
||||
});
|
||||
|
||||
const isEdited = computed<boolean>(() => {
|
||||
return props.modelValue !== undefined && isEqual(props.modelValue, props.initialValue) === false;
|
||||
});
|
||||
const { internalValue, isEdited, defaultValue } = useComputedValues();
|
||||
|
||||
const { showRaw, rawValue, copyRaw, pasteRaw } = useRaw();
|
||||
|
||||
@@ -221,6 +206,34 @@ function useRaw() {
|
||||
|
||||
return { showRaw, rawValue, copyRaw, pasteRaw };
|
||||
}
|
||||
|
||||
function useComputedValues() {
|
||||
const defaultValue = computed<any>(() => props.field?.schema?.default_value);
|
||||
const internalValue = ref<any>(getInternalValue());
|
||||
const isEdited = ref<boolean>(getIsEdited());
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
const newVal = getInternalValue();
|
||||
if (!isEqual(internalValue.value, newVal)) {
|
||||
internalValue.value = newVal;
|
||||
}
|
||||
isEdited.value = getIsEdited();
|
||||
}
|
||||
);
|
||||
|
||||
return { internalValue, isEdited, defaultValue };
|
||||
|
||||
function getInternalValue(): any {
|
||||
if (props.modelValue !== undefined) return props.modelValue;
|
||||
if (props.initialValue !== undefined) return props.initialValue;
|
||||
return defaultValue.value;
|
||||
}
|
||||
function getIsEdited(): boolean {
|
||||
return props.modelValue !== undefined && isEqual(props.modelValue, props.initialValue) === false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -79,7 +79,7 @@ import { applyConditions } from '@/utils/apply-conditions';
|
||||
import { extractFieldFromFunction } from '@/utils/extract-field-from-function';
|
||||
import { Field, FieldMeta, ValidationError } from '@directus/shared/types';
|
||||
import { assign, cloneDeep, isEqual, isNil, omit, pick } from 'lodash';
|
||||
import { computed, defineComponent, onBeforeUpdate, PropType, provide, ref, watch, unref } from 'vue';
|
||||
import { computed, defineComponent, onBeforeUpdate, PropType, provide, ref, watch, Ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FormField from './form-field.vue';
|
||||
import ValidationErrors from './validation-errors.vue';
|
||||
@@ -150,19 +150,7 @@ export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const fields = computed(() => {
|
||||
if (props.collection) {
|
||||
return fieldsStore.getFieldsForCollection(props.collection);
|
||||
}
|
||||
|
||||
if (props.fields) {
|
||||
return props.fields;
|
||||
}
|
||||
|
||||
throw new Error('[v-form]: You need to pass either the collection or fields prop.');
|
||||
});
|
||||
const fields = useComputedFields();
|
||||
|
||||
const values = computed(() => {
|
||||
return Object.assign({}, props.initialValues, props.modelValue);
|
||||
@@ -328,6 +316,35 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
function useComputedFields(): Ref<Field[]> {
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const fields = ref<Field[]>(getFields());
|
||||
|
||||
watch(
|
||||
() => props.fields,
|
||||
() => {
|
||||
const newVal = getFields();
|
||||
if (!isEqual(fields.value, newVal)) {
|
||||
fields.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return fields;
|
||||
|
||||
function getFields(): Field[] {
|
||||
if (props.collection) {
|
||||
return fieldsStore.getFieldsForCollection(props.collection);
|
||||
}
|
||||
if (props.fields) {
|
||||
return props.fields;
|
||||
}
|
||||
|
||||
throw new Error('[v-form]: You need to pass either the collection or fields prop.');
|
||||
}
|
||||
}
|
||||
|
||||
function setValue(fieldKey: string, value: any) {
|
||||
const field = formFields.value?.find((field) => field.field === fieldKey);
|
||||
|
||||
|
||||
@@ -106,19 +106,8 @@ export default defineComponent({
|
||||
|
||||
const fieldsInSection = computed(() => {
|
||||
return props.fields
|
||||
.filter((field) => {
|
||||
if (field.meta?.group === props.group && field.meta?.id !== props.field.meta?.id) return false;
|
||||
return true;
|
||||
})
|
||||
.map((field) => {
|
||||
if (field.meta?.id === props.field.meta?.id) {
|
||||
return merge({}, field, {
|
||||
hideLabel: true,
|
||||
});
|
||||
}
|
||||
|
||||
return field;
|
||||
});
|
||||
.filter((field) => field.meta?.group === props.group && field.meta?.id === props.field.meta?.id)
|
||||
.map((field) => merge({}, field, { hideLabel: true }));
|
||||
});
|
||||
|
||||
const edited = computed(() => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<v-item-group v-model="selection" scope="group-accordion" class="group-accordion" :multiple="accordionMode === false">
|
||||
<accordion-section
|
||||
v-for="accordionField in rootFields"
|
||||
v-for="accordionField in groupFields"
|
||||
:key="accordionField.field"
|
||||
:field="accordionField"
|
||||
:fields="fields"
|
||||
:values="values"
|
||||
:fields="groupFields"
|
||||
:values="groupValues"
|
||||
:initial-values="initialValues"
|
||||
:disabled="disabled"
|
||||
:batch-mode="batchMode"
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Field } from '@directus/shared/types';
|
||||
import { defineComponent, PropType, computed, ref, watch } from 'vue';
|
||||
import { defineComponent, PropType, ref, watch } from 'vue';
|
||||
import { ValidationError } from '@directus/shared/types';
|
||||
import AccordionSection from './accordion-section.vue';
|
||||
import { isEqual } from 'lodash';
|
||||
@@ -77,7 +77,6 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
|
||||
accordionMode: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@@ -90,21 +89,18 @@ export default defineComponent({
|
||||
},
|
||||
emits: ['apply'],
|
||||
setup(props) {
|
||||
const rootFields = computed(() => {
|
||||
return props.fields.filter((field) => field.meta?.group === props.field.meta?.field);
|
||||
});
|
||||
|
||||
const selection = ref<string[]>([]);
|
||||
const { groupFields, groupValues } = useComputedGroup();
|
||||
|
||||
watch(
|
||||
() => props.start,
|
||||
(start) => {
|
||||
if (start === 'opened') {
|
||||
selection.value = rootFields.value.map((field) => field.field);
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
|
||||
if (start === 'first') {
|
||||
selection.value = [rootFields.value[0].field];
|
||||
selection.value = [groupFields.value[0].field];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
@@ -116,21 +112,50 @@ export default defineComponent({
|
||||
if (!props.validationErrors) return;
|
||||
if (isEqual(newVal, oldVal)) return;
|
||||
const includedFieldsWithErrors = props.validationErrors.filter((validationError) =>
|
||||
rootFields.value.find((rootField) => rootField.field === validationError.field)
|
||||
groupFields.value.find((rootField) => rootField.field === validationError.field)
|
||||
);
|
||||
if (includedFieldsWithErrors.length > 0) selection.value = [includedFieldsWithErrors[0].field];
|
||||
}
|
||||
);
|
||||
|
||||
return { rootFields, selection, toggleAll };
|
||||
return { groupFields, groupValues, selection, toggleAll };
|
||||
|
||||
function toggleAll() {
|
||||
if (props.accordionMode === true) return;
|
||||
|
||||
if (selection.value.length === rootFields.value.length) {
|
||||
if (selection.value.length === groupFields.value.length) {
|
||||
selection.value = [];
|
||||
} else {
|
||||
selection.value = rootFields.value.map((field) => field.field);
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
}
|
||||
|
||||
function useComputedGroup() {
|
||||
const groupFields = ref<Field[]>(limitFields());
|
||||
const groupValues = ref<Record<string, any>>({});
|
||||
|
||||
watch(
|
||||
() => props.fields,
|
||||
() => {
|
||||
const newVal = limitFields();
|
||||
if (!isEqual(groupFields.value, newVal)) {
|
||||
groupFields.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.values,
|
||||
(newVal) => {
|
||||
if (!isEqual(groupValues.value, newVal)) {
|
||||
groupValues.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { groupFields, groupValues };
|
||||
|
||||
function limitFields(): Field[] {
|
||||
return props.fields.filter((field) => field.meta?.group === props.field.meta?.field);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user