mirror of
https://github.com/directus/directus.git
synced 2026-02-19 10:14:33 -05:00
Many to Many (#675)
* Start on files inteface * Return related m2m relation in store * Add useRelations to files * Fetch current items in files * Allow dot notation in table row * Render tableHeaders * Add file display * Register file display * Remove unused css * Rough in select modal * Start on saving selected items * Finish selecting existing item * Auto fix line width of file display * Rename var to be clearer * Rework to allow editing junctino row * Add support for junction fields in modal detail * Stage changes to existing items correctly * Show previously made edits in modal detail * Blammo bunch of changes, lets do m2m first * Stage newly created items * Stage newly created items * Add editing of newly added records * Stage new edits of existing items * Allow updating existing rows * Stage selection of newly selected items * Add fetching preview for related item * Allow for updating staged selected existing items * Fix nested m2o in m2m * Finish deep fetch of multi-related item * Finish deselecting of rows * Add a little note to future self
This commit is contained in:
@@ -8,6 +8,18 @@
|
||||
v-model="_edits"
|
||||
/>
|
||||
|
||||
<template v-if="junctionField">
|
||||
<v-divider large>{{ junctionFieldInfo.name }}</v-divider>
|
||||
<v-form
|
||||
:loading="loading"
|
||||
:initial-values="item && item[junctionField]"
|
||||
:collection="junctionRelatedCollection"
|
||||
:primary-key="relatedPrimaryKey"
|
||||
:edits="_edits[junctionField]"
|
||||
@input="setJunctionEdits"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<v-button @click="cancel" secondary>{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="save">{{ $t('save') }}</v-button>
|
||||
@@ -20,7 +32,10 @@ import { defineComponent, ref, computed, PropType, watch, toRefs } from '@vue/co
|
||||
import api from '@/api';
|
||||
import useProjectsStore from '@/stores/projects';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import useFieldsStore from '@/stores/fields';
|
||||
import i18n from '@/lang';
|
||||
import useRelationsStore from '@/stores/relations';
|
||||
import { Relation } from '@/stores/relations/types';
|
||||
|
||||
export default defineComponent({
|
||||
model: {
|
||||
@@ -43,11 +58,25 @@ export default defineComponent({
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
default: undefined,
|
||||
},
|
||||
junctionField: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
// There's an interesting case where the main form can be a newly created item ('+'), while
|
||||
// it has a pre-selected related item it needs to alter. In that case, we have to fetch the
|
||||
// related data anyway.
|
||||
relatedPrimaryKey: {
|
||||
type: [String, Number],
|
||||
default: '+',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const projectsStore = useProjectsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
const { _active } = useActiveState();
|
||||
const { junctionFieldInfo, junctionRelatedCollection, setJunctionEdits } = useJunction();
|
||||
const { _edits, loading, error, item } = useItem();
|
||||
const { save, cancel } = useActions();
|
||||
|
||||
@@ -63,7 +92,19 @@ export default defineComponent({
|
||||
return i18n.t('editing_in', { collection: collectionInfo.value?.name });
|
||||
});
|
||||
|
||||
return { _active, _edits, loading, error, item, save, cancel, title };
|
||||
return {
|
||||
_active,
|
||||
_edits,
|
||||
loading,
|
||||
error,
|
||||
item,
|
||||
save,
|
||||
cancel,
|
||||
title,
|
||||
junctionFieldInfo,
|
||||
junctionRelatedCollection,
|
||||
setJunctionEdits,
|
||||
};
|
||||
|
||||
function useActiveState() {
|
||||
const localActive = ref(false);
|
||||
@@ -109,6 +150,7 @@ export default defineComponent({
|
||||
(isActive) => {
|
||||
if (isActive === true) {
|
||||
if (props.primaryKey !== '+') fetchItem();
|
||||
if (props.relatedPrimaryKey !== '+') fetchRelatedItem();
|
||||
} else {
|
||||
loading.value = false;
|
||||
error.value = null;
|
||||
@@ -129,8 +171,14 @@ export default defineComponent({
|
||||
? `/${currentProjectKey}/${props.collection.substring(9)}/${props.primaryKey}`
|
||||
: `/${currentProjectKey}/items/${props.collection}/${props.primaryKey}`;
|
||||
|
||||
let fields = '*';
|
||||
|
||||
if (props.junctionField) {
|
||||
fields = `*,${props.junctionField}.*`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.get(endpoint);
|
||||
const response = await api.get(endpoint, { params: { fields } });
|
||||
|
||||
item.value = response.data.data;
|
||||
} catch (err) {
|
||||
@@ -139,6 +187,64 @@ export default defineComponent({
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchRelatedItem() {
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const collection = junctionRelatedCollection.value;
|
||||
|
||||
const endpoint = collection.startsWith('directus_')
|
||||
? `/${currentProjectKey}/${collection.substring(9)}/${props.relatedPrimaryKey}`
|
||||
: `/${currentProjectKey}/items/${collection}/${props.relatedPrimaryKey}`;
|
||||
|
||||
try {
|
||||
const response = await api.get(endpoint);
|
||||
|
||||
item.value = {
|
||||
...(item.value || {}),
|
||||
[junctionFieldInfo.value.field]: response.data.data,
|
||||
};
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useJunction() {
|
||||
const junctionFieldInfo = computed(() => {
|
||||
if (!props.junctionField) return null;
|
||||
|
||||
return fieldsStore.getField(props.collection, props.junctionField);
|
||||
});
|
||||
|
||||
const junctionRelatedCollection = computed(() => {
|
||||
if (!props.junctionField) return null;
|
||||
|
||||
// If this is a m2m (likely), there will be 2 relations associated with this field
|
||||
const relations = relationsStore.getRelationsForField(props.collection, props.junctionField);
|
||||
return (
|
||||
relations.find((relation: Relation) => {
|
||||
return (
|
||||
relation.collection_many === props.collection && relation.field_many === props.junctionField
|
||||
);
|
||||
})?.collection_one || null
|
||||
);
|
||||
});
|
||||
|
||||
return { junctionFieldInfo, junctionRelatedCollection, setJunctionEdits };
|
||||
|
||||
function setJunctionEdits(edits: any) {
|
||||
if (!props.junctionField) return;
|
||||
|
||||
_edits.value = {
|
||||
..._edits.value,
|
||||
[props.junctionField]: edits,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function useActions() {
|
||||
@@ -158,3 +264,9 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.v-divider {
|
||||
margin: 52px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user