mirror of
https://github.com/directus/directus.git
synced 2026-01-25 01:48:06 -05:00
Allow for displaying lists in render template (#15243)
* support arrays in render-template * allow for rendering lists in render-template
This commit is contained in:
@@ -20,8 +20,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed, ref } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useFieldsStore } from '@/stores/fields';
|
||||
import { get } from 'lodash';
|
||||
import { Field } from '@directus/shared/types';
|
||||
@@ -29,96 +29,123 @@ import { getDisplay } from '@/displays';
|
||||
import { getDefaultDisplayForType } from '@/utils/get-default-display-for-type';
|
||||
import { translate } from '@/utils/translate-object-values';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
collection: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
fields: {
|
||||
type: Array as PropType<Field[]>,
|
||||
default: null,
|
||||
},
|
||||
item: {
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
default: null,
|
||||
},
|
||||
template: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const fieldsStore = useFieldsStore();
|
||||
interface Props {
|
||||
template: string;
|
||||
collection?: string;
|
||||
fields?: Field[];
|
||||
item?: Record<string, any>;
|
||||
direction?: string;
|
||||
}
|
||||
|
||||
const templateEl = ref<HTMLElement>();
|
||||
|
||||
const regex = /({{.*?}})/g;
|
||||
|
||||
const parts = computed(() =>
|
||||
props.template
|
||||
.split(regex)
|
||||
.filter((p) => p)
|
||||
.map((part) => {
|
||||
if (part.startsWith('{{') === false) return part;
|
||||
|
||||
let fieldKey = part.replace(/{{/g, '').replace(/}}/g, '').trim();
|
||||
const field: Field | undefined =
|
||||
fieldsStore.getField(props.collection, fieldKey) || props.fields?.find((field) => field.field === fieldKey);
|
||||
|
||||
/**
|
||||
* This is for cases where you are rendering a display template directly on
|
||||
* directus_files. The $thumbnail fields doesn't exist, but instead renders a
|
||||
* thumbnail based on the other fields in the file info. In that case, the value
|
||||
* should be the whole related file object, not just the fake "thumbnail" field. By
|
||||
* stripping out the thumbnail part in the field key path, the rest of the function
|
||||
* will extract the value correctly.
|
||||
*/
|
||||
if (field && field.collection === 'directus_files' && field.field === '$thumbnail') {
|
||||
fieldKey = fieldKey
|
||||
.split('.')
|
||||
.filter((part) => part !== '$thumbnail')
|
||||
.join('.');
|
||||
}
|
||||
|
||||
// Try getting the value from the item, return some question marks if it doesn't exist
|
||||
const value = get(props.item, fieldKey);
|
||||
|
||||
if (value === undefined) return null;
|
||||
|
||||
if (!field) return value;
|
||||
|
||||
const display = field?.meta?.display || getDefaultDisplayForType(field.type);
|
||||
|
||||
// No need to render the empty display overhead in this case
|
||||
if (display === 'raw') return value;
|
||||
|
||||
const displayInfo = getDisplay(field.meta?.display);
|
||||
|
||||
// If used display doesn't exist in the current project, return raw value
|
||||
if (!displayInfo) return value;
|
||||
|
||||
return {
|
||||
component: field.meta?.display,
|
||||
options: field.meta?.display_options,
|
||||
value: value,
|
||||
interface: field.meta?.interface,
|
||||
interfaceOptions: field.meta?.options,
|
||||
type: field.type,
|
||||
collection: field.collection,
|
||||
field: field.field,
|
||||
};
|
||||
})
|
||||
.map((p) => p ?? null)
|
||||
);
|
||||
|
||||
return { parts, templateEl, translate };
|
||||
},
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
collection: undefined,
|
||||
fields: () => [],
|
||||
item: () => ({}),
|
||||
direction: undefined,
|
||||
});
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const templateEl = ref<HTMLElement>();
|
||||
|
||||
const regex = /({{.*?}})/g;
|
||||
|
||||
const parts = computed(() =>
|
||||
props.template
|
||||
.split(regex)
|
||||
.filter((p) => p)
|
||||
.map((part) => {
|
||||
if (part.startsWith('{{') === false) return part;
|
||||
|
||||
let fieldKey = part.replace(/{{/g, '').replace(/}}/g, '').trim();
|
||||
let fieldKeyBefore = fieldKey.split('.').slice(0, -1).join('.');
|
||||
let fieldKeyAfter = fieldKey.split('.').slice(-1)[0];
|
||||
|
||||
// Try getting the value from the item, return some question marks if it doesn't exist
|
||||
let value = get(props.item, fieldKeyBefore);
|
||||
|
||||
return Array.isArray(value) ? handleArray(fieldKeyBefore, fieldKeyAfter) : handleObject(fieldKey);
|
||||
})
|
||||
.map((p) => p ?? null)
|
||||
);
|
||||
|
||||
function handleArray(fieldKeyBefore: string, fieldKeyAfter: string) {
|
||||
const value = get(props.item, fieldKeyBefore);
|
||||
const field =
|
||||
fieldsStore.getField(props.collection, fieldKeyBefore) ||
|
||||
props.fields?.find((field) => field.field === fieldKeyBefore);
|
||||
|
||||
if (value === undefined) return null;
|
||||
|
||||
if (!field) return value;
|
||||
|
||||
const displayInfo = getDisplay(field.meta?.display);
|
||||
|
||||
let component = field.meta?.display;
|
||||
let options = field.meta?.display_options;
|
||||
|
||||
if (!displayInfo) {
|
||||
component = 'related-values';
|
||||
options = { template: `{{${fieldKeyAfter}}}` };
|
||||
}
|
||||
|
||||
return {
|
||||
component,
|
||||
options,
|
||||
value: value,
|
||||
interface: field.meta?.interface,
|
||||
interfaceOptions: field.meta?.options,
|
||||
type: field.type,
|
||||
collection: field.collection,
|
||||
field: field.field,
|
||||
};
|
||||
}
|
||||
|
||||
function handleObject(fieldKey: string) {
|
||||
const value = get(props.item, fieldKey);
|
||||
const field =
|
||||
fieldsStore.getField(props.collection, fieldKey) || props.fields?.find((field) => field.field === fieldKey);
|
||||
|
||||
if (value === undefined) return null;
|
||||
|
||||
/**
|
||||
* This is for cases where you are rendering a display template directly on
|
||||
* directus_files. The $thumbnail fields doesn't exist, but instead renders a
|
||||
* thumbnail based on the other fields in the file info. In that case, the value
|
||||
* should be the whole related file object, not just the fake "thumbnail" field. By
|
||||
* stripping out the thumbnail part in the field key path, the rest of the function
|
||||
* will extract the value correctly.
|
||||
*/
|
||||
if (field && field.collection === 'directus_files' && field.field === '$thumbnail') {
|
||||
fieldKey = fieldKey
|
||||
.split('.')
|
||||
.filter((part) => part !== '$thumbnail')
|
||||
.join('.');
|
||||
}
|
||||
|
||||
if (!field) return value;
|
||||
|
||||
const display = field?.meta?.display || getDefaultDisplayForType(field.type);
|
||||
|
||||
// No need to render the empty display overhead in this case
|
||||
if (display === 'raw') return value;
|
||||
|
||||
const displayInfo = getDisplay(field.meta?.display);
|
||||
|
||||
// If used display doesn't exist in the current project, return raw value
|
||||
if (!displayInfo) return value;
|
||||
|
||||
return {
|
||||
component: field.meta?.display,
|
||||
options: field.meta?.display_options,
|
||||
value: value,
|
||||
interface: field.meta?.interface,
|
||||
interfaceOptions: field.meta?.options,
|
||||
type: field.type,
|
||||
collection: field.collection,
|
||||
field: field.field,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user