fix and clean up m2m & o2m (#15220)

This commit is contained in:
Nitwel
2022-08-24 23:52:04 +02:00
committed by GitHub
parent e605f2cd64
commit f91247260a
2 changed files with 97 additions and 57 deletions

View File

@@ -12,7 +12,11 @@
</div>
<div v-if="enableSearchFilter && (totalItemCount > 10 || search || searchFilter)" class="search">
<search-input v-model="search" v-model:filter="searchFilter" :collection="junctionCollection" />
<search-input
v-model="search"
v-model:filter="searchFilter"
:collection="relationInfo.junctionCollection.collection"
/>
</div>
<v-button
@@ -51,7 +55,7 @@
<template v-for="header in headers" :key="header.value" #[`item.${header.value}`]="{ item }">
<render-template
:title="header.value"
:collection="junctionCollection"
:collection="relationInfo.junctionCollection.collection"
:item="item"
:template="`{{${header.value}}}`"
/>
@@ -110,7 +114,11 @@
@click="editItem(element)"
>
<v-icon v-if="allowDrag" name="drag_handle" class="drag-handle" left @click.stop="() => {}" />
<render-template :collection="junctionCollection" :item="element" :template="templateWithDefaults" />
<render-template
:collection="relationInfo.junctionCollection.collection"
:item="element"
:template="templateWithDefaults"
/>
<div class="spacer" />
<router-link
@@ -155,10 +163,10 @@
<drawer-item
:disabled="disabled"
:active="editModalActive"
:collection="junctionCollection"
:collection="relationInfo.junctionCollection.collection"
:primary-key="currentlyEditing || '+'"
:related-primary-key="relatedPrimaryKey || '+'"
:junction-field="junctionField"
:junction-field="relationInfo.junctionField.field"
:edits="editsAtStart"
:circular-field="relationInfo.reverseJunctionField.field"
@input="stageEdits"
@@ -168,7 +176,7 @@
<drawer-collection
v-if="!disabled"
v-model:active="selectModalActive"
:collection="relatedCollection"
:collection="relationInfo.relatedCollection.collection"
:filter="customFilter"
multiple
@input="select"
@@ -180,7 +188,7 @@
import { useRelationM2M } from '@/composables/use-relation-m2m';
import { useRelationMultiple, RelationQueryMultiple, DisplayItem } from '@/composables/use-relation-multiple';
import { parseFilter } from '@/utils/parse-filter';
import { CollectionMeta, Filter } from '@directus/shared/types';
import { Filter } from '@directus/shared/types';
import { deepMap, getFieldsFromTemplate } from '@directus/shared/utils';
import { render } from 'micromustache';
import { computed, inject, ref, toRefs, watch } from 'vue';
@@ -242,15 +250,6 @@ const { collection, field, primaryKey } = toRefs(props);
const { relationInfo } = useRelationM2M(collection, field);
const fieldsStore = useFieldsStore();
const relatedCollection = computed(() => relationInfo.value?.relatedCollection.collection ?? '');
const relatedPkField = computed(() => relationInfo.value?.relatedPrimaryKeyField.field ?? 'id');
const relatedMeta = computed(() => relationInfo.value?.relatedCollection.meta ?? ({} as CollectionMeta));
const junctionCollection = computed(() => relationInfo.value?.junctionCollection.collection ?? '');
const junctionPkField = computed(() => relationInfo.value?.junctionPrimaryKeyField.field ?? 'id');
const junctionMeta = computed(() => relationInfo.value?.junctionCollection.meta ?? ({} as CollectionMeta));
const junctionField = computed(() => relationInfo.value?.junctionField.field ?? '');
const value = computed({
get: () => props.value,
set: (val) => {
@@ -262,9 +261,10 @@ const templateWithDefaults = computed(() => {
if (!relationInfo.value) return null;
if (props.template) return props.template;
if (junctionMeta.value.display_template) return junctionMeta.value.display_template;
if (relationInfo.value.junctionCollection.meta?.display_template)
return relationInfo.value.junctionCollection.meta.display_template;
let relatedDisplayTemplate = relatedMeta.value.display_template;
let relatedDisplayTemplate = relationInfo.value.relatedCollection.meta?.display_template;
if (relatedDisplayTemplate) {
const regex = /({{.*?}})/g;
const parts = relatedDisplayTemplate.split(regex).filter((p) => p);
@@ -280,21 +280,23 @@ const templateWithDefaults = computed(() => {
return relatedDisplayTemplate;
}
return `{{${relationInfo.value.relation.field}.${relatedPkField.value}}}`;
return `{{${relationInfo.value.relation.field}.${relationInfo.value.relatedPrimaryKeyField.field}}}`;
});
const fields = computed(() => {
if (!relationInfo.value) return [];
let displayFields: string[] = [];
if (props.layout === LAYOUTS.TABLE) {
displayFields = adjustFieldsForDisplays(props.fields, junctionCollection.value);
displayFields = adjustFieldsForDisplays(props.fields, relationInfo.value.junctionCollection.collection);
} else {
displayFields = adjustFieldsForDisplays(
getFieldsFromTemplate(templateWithDefaults.value),
junctionCollection.value
relationInfo.value.junctionCollection.collection
);
}
return addRelatedPrimaryKeyToFields(junctionCollection.value, displayFields);
return addRelatedPrimaryKeyToFields(relationInfo.value.junctionCollection.collection, displayFields);
});
const limit = ref(props.limit);
@@ -320,7 +322,7 @@ const query = computed<RelationQueryMultiple>(() => {
q.search = search.value;
}
if (sort.value) {
q.sort = [`${sort.value.desc ? '-' : ''}${junctionField.value}.${sort.value.by}`];
q.sort = [`${sort.value.desc ? '-' : ''}${relationInfo.value.junctionField.field}.${sort.value.by}`];
}
return q;
@@ -351,8 +353,11 @@ watch(
() => {
if (!relationInfo.value) {
headers.value = [];
return;
}
const junctionCollection = relationInfo.value.junctionCollection.collection;
const contentWidth: Record<string, number> = {};
(displayItems.value ?? []).forEach((item: Record<string, any>) => {
props.fields.forEach((key) => {
@@ -367,7 +372,7 @@ watch(
headers.value = props.fields
.map((key) => {
const field = fieldsStore.getField(junctionCollection.value, key);
const field = fieldsStore.getField(junctionCollection, key);
// when user has no permission to this field or junction collection
if (!field) return null;
@@ -422,7 +427,10 @@ function sortItems(items: DisplayItem[]) {
const selectedPrimaryKeys = computed(() => {
if (!relationInfo.value) return [];
return selected.value.map((item) => item[junctionField.value][relatedPkField.value]);
const junctionField = relationInfo.value.junctionField.field;
const relatedPkField = relationInfo.value.relatedPrimaryKeyField.field;
return selected.value.map((item) => item[junctionField][relatedPkField]);
});
const editModalActive = ref(false);
@@ -443,6 +451,10 @@ function createItem() {
function editItem(item: DisplayItem) {
if (!relationInfo.value) return;
const relatedPkField = relationInfo.value.relatedPrimaryKeyField.field;
const junctionField = relationInfo.value.junctionField.field;
const junctionPkField = relationInfo.value.junctionPrimaryKeyField.field;
newItem = false;
editsAtStart.value = item;
@@ -452,8 +464,8 @@ function editItem(item: DisplayItem) {
currentlyEditing.value = null;
relatedPrimaryKey.value = null;
} else {
currentlyEditing.value = get(item, [junctionPkField.value], null);
relatedPrimaryKey.value = get(item, [junctionPkField.value, relatedPkField.value], null);
currentlyEditing.value = get(item, [junctionPkField], null);
relatedPrimaryKey.value = get(item, [junctionField, relatedPkField], null);
}
}
@@ -504,7 +516,7 @@ const customFilter = computed(() => {
if (!relationInfo.value || props.allowDuplicates) return filter;
const reverseRelation = `$FOLLOW(${junctionCollection.value},${junctionField.value})`;
const reverseRelation = `$FOLLOW(${relationInfo.value.junctionCollection.collection},${relationInfo.value.junctionField.field})`;
const selectFilter: Filter = {
[reverseRelation]: {
@@ -518,7 +530,7 @@ const customFilter = computed(() => {
if (selectedPrimaryKeys.value.length > 0) {
filter._and.push({
[relatedPkField.value]: {
[relationInfo.value.relatedPrimaryKeyField.field]: {
_nin: selectedPrimaryKeys.value,
},
});
@@ -531,8 +543,11 @@ const customFilter = computed(() => {
function getLinkForItem(item: DisplayItem) {
if (relationInfo.value) {
const primaryKey = get(item, [junctionField.value, relatedPkField.value]);
return `/content/${relatedCollection.value}/${encodeURIComponent(primaryKey)}`;
const primaryKey = get(item, [
relationInfo.value.junctionField.field,
relationInfo.value.relatedPrimaryKeyField.field,
]);
return `/content/${relationInfo.value.relatedCollection.collection}/${encodeURIComponent(primaryKey)}`;
}
return null;
@@ -546,11 +561,13 @@ const createAllowed = computed(() => {
if (admin) return true;
const hasJunctionPermissions = !!permissionsStore.permissions.find(
(permission) => permission.action === 'create' && permission.collection === junctionCollection.value
(permission) =>
permission.action === 'create' && permission.collection === relationInfo.value?.junctionCollection.collection
);
const hasRelatedPermissions = !!permissionsStore.permissions.find(
(permission) => permission.action === 'create' && permission.collection === relatedCollection.value
(permission) =>
permission.action === 'create' && permission.collection === relationInfo.value?.relatedCollection.collection
);
return hasJunctionPermissions && hasRelatedPermissions;
@@ -561,7 +578,8 @@ const selectAllowed = computed(() => {
if (admin) return true;
const hasJunctionPermissions = !!permissionsStore.permissions.find(
(permission) => permission.action === 'create' && permission.collection === junctionCollection.value
(permission) =>
permission.action === 'create' && permission.collection === relationInfo.value?.junctionCollection.collection
);
return hasJunctionPermissions;

View File

@@ -12,7 +12,11 @@
</div>
<div v-if="enableSearchFilter && (totalItemCount > 10 || search || searchFilter)" class="search">
<search-input v-model="search" v-model:filter="searchFilter" :collection="relatedCollection" />
<search-input
v-model="search"
v-model:filter="searchFilter"
:collection="relationInfo.relatedCollection.collection"
/>
</div>
<v-button
@@ -51,7 +55,7 @@
<template v-for="header in headers" :key="header.value" #[`item.${header.value}`]="{ item }">
<render-template
:title="header.value"
:collection="relatedCollection"
:collection="relationInfo.relatedCollection.collection"
:item="item"
:template="`{{${header.value}}}`"
/>
@@ -109,7 +113,11 @@
@click="editItem(element)"
>
<v-icon v-if="allowDrag" name="drag_handle" class="drag-handle" left @click.stop="() => {}" />
<render-template :collection="relatedCollection" :item="element" :template="templateWithDefaults" />
<render-template
:collection="relationInfo.relatedCollection.collection"
:item="element"
:template="templateWithDefaults"
/>
<div class="spacer" />
<router-link
@@ -154,7 +162,7 @@
<drawer-item
:disabled="disabled"
:active="currentlyEditing !== null"
:collection="relatedCollection"
:collection="relationInfo.relatedCollection.collection"
:primary-key="currentlyEditing || '+'"
:edits="editsAtStart"
:circular-field="relationInfo.reverseJunctionField.field"
@@ -165,7 +173,7 @@
<drawer-collection
v-if="!disabled"
v-model:active="selectModalActive"
:collection="relatedCollection"
:collection="relationInfo.relatedCollection.collection"
:filter="customFilter"
multiple
@input="select"
@@ -177,7 +185,7 @@
import { useRelationO2M } from '@/composables/use-relation-o2m';
import { useRelationMultiple, RelationQueryMultiple, DisplayItem } from '@/composables/use-relation-multiple';
import { parseFilter } from '@/utils/parse-filter';
import { CollectionMeta, Filter } from '@directus/shared/types';
import { Filter } from '@directus/shared/types';
import { deepMap, getFieldsFromTemplate } from '@directus/shared/utils';
import { render } from 'micromustache';
import { computed, inject, ref, toRefs, watch } from 'vue';
@@ -237,10 +245,6 @@ const { collection, field, primaryKey } = toRefs(props);
const { relationInfo } = useRelationO2M(collection, field);
const fieldsStore = useFieldsStore();
const relatedCollection = computed(() => relationInfo.value?.relatedCollection.collection ?? '');
const relatedPkField = computed(() => relationInfo.value?.relatedPrimaryKeyField.field ?? 'id');
const relatedMeta = computed(() => relationInfo.value?.relatedCollection.meta ?? ({} as CollectionMeta));
const value = computed({
get: () => props.value,
set: (val) => {
@@ -249,18 +253,27 @@ const value = computed({
});
const templateWithDefaults = computed(() => {
return props.template || relatedMeta.value.display_template || `{{${relatedPkField.value}}}`;
return (
props.template ||
relationInfo.value?.relatedCollection.meta?.display_template ||
`{{${relationInfo.value?.relatedPrimaryKeyField.field}}}`
);
});
const fields = computed(() => {
if (!relationInfo.value) return [];
let displayFields: string[] = [];
if (props.layout === LAYOUTS.TABLE) {
displayFields = adjustFieldsForDisplays(props.fields, relatedCollection.value);
displayFields = adjustFieldsForDisplays(props.fields, relationInfo.value.relatedCollection.collection);
} else {
displayFields = adjustFieldsForDisplays(getFieldsFromTemplate(templateWithDefaults.value), relatedCollection.value);
displayFields = adjustFieldsForDisplays(
getFieldsFromTemplate(templateWithDefaults.value),
relationInfo.value.relatedCollection.collection
);
}
return addRelatedPrimaryKeyToFields(relatedCollection.value, displayFields);
return addRelatedPrimaryKeyToFields(relationInfo.value.relatedCollection.collection, displayFields);
});
const limit = ref(props.limit);
@@ -317,8 +330,11 @@ watch(
() => {
if (!relationInfo.value) {
headers.value = [];
return;
}
const relatedCollection = relationInfo.value.relatedCollection.collection;
const contentWidth: Record<string, number> = {};
(displayItems.value ?? []).forEach((item: Record<string, any>) => {
props.fields.forEach((key) => {
@@ -333,7 +349,7 @@ watch(
headers.value = props.fields
.map((key) => {
const field = fieldsStore.getField(relatedCollection.value, key);
const field = fieldsStore.getField(relatedCollection, key);
// when user has no permission to this field or junction collection
if (!field) return null;
@@ -388,7 +404,9 @@ function sortItems(items: DisplayItem[]) {
const selectedPrimaryKeys = computed(() => {
if (!relationInfo.value) return [];
return selected.value.map((item) => item[relatedPkField.value]);
const relatedPkField = relationInfo.value.relatedPrimaryKeyField.field;
return selected.value.map((item) => item[relatedPkField]);
});
const currentlyEditing = ref<string | null>(null);
@@ -405,13 +423,15 @@ function createItem() {
function editItem(item: DisplayItem) {
if (!relationInfo.value) return;
const relatedPkField = relationInfo.value.relatedPrimaryKeyField.field;
newItem = false;
editsAtStart.value = { [relatedPkField.value]: item[relatedPkField.value] };
editsAtStart.value = { [relatedPkField]: item[relatedPkField] };
if (item?.$type === 'created' && !isItemSelected(item)) {
currentlyEditing.value = '+';
} else {
currentlyEditing.value = item[relatedPkField.value];
currentlyEditing.value = item[relatedPkField];
}
}
@@ -479,7 +499,7 @@ const customFilter = computed(() => {
if (selectedPrimaryKeys.value.length > 0) {
filter._and.push({
[relatedPkField.value]: {
[relationInfo.value.relatedPrimaryKeyField.field]: {
_nin: selectedPrimaryKeys.value,
},
});
@@ -492,8 +512,8 @@ const customFilter = computed(() => {
function getLinkForItem(item: DisplayItem) {
if (relationInfo.value) {
const primaryKey = get(item, relatedPkField.value);
return `/content/${relatedCollection.value}/${encodeURIComponent(primaryKey)}`;
const primaryKey = get(item, relationInfo.value.relatedPrimaryKeyField.field);
return `/content/${relationInfo.value.relatedCollection.collection}/${encodeURIComponent(primaryKey)}`;
}
return null;
@@ -507,7 +527,8 @@ const createAllowed = computed(() => {
if (admin) return true;
return !!permissionsStore.permissions.find(
(permission) => permission.action === 'create' && permission.collection === relatedCollection.value
(permission) =>
permission.action === 'create' && permission.collection === relationInfo.value?.relatedCollection.collection
);
});
@@ -516,7 +537,8 @@ const updateAllowed = computed(() => {
if (admin) return true;
return !!permissionsStore.permissions.find(
(permission) => permission.action === 'update' && permission.collection === relatedCollection.value
(permission) =>
permission.action === 'update' && permission.collection === relationInfo.value?.relatedCollection.collection
);
});
</script>