mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(editor): Implement loading and error states for dynamically loaded components in node parameter list (#8477)
This commit is contained in:
parent
121a55b691
commit
e643a126f4
|
@ -49,7 +49,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, defineAsyncComponent } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import type { IUpdateInformation } from '@/Interface';
|
import type { IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -65,9 +65,6 @@ import { get } from 'lodash-es';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
const ParameterInputList = defineAsyncComponent(
|
|
||||||
async () => await import('./ParameterInputList.vue'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedOption = ref<string | undefined>(undefined);
|
const selectedOption = ref<string | undefined>(undefined);
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
|
|
@ -122,7 +122,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import type { IUpdateInformation } from '@/Interface';
|
import type { IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
|
@ -136,15 +136,8 @@ import { deepCopy, isINodePropertyCollectionList } from 'n8n-workflow';
|
||||||
|
|
||||||
import { get } from 'lodash-es';
|
import { get } from 'lodash-es';
|
||||||
|
|
||||||
const ParameterInputList = defineAsyncComponent(
|
|
||||||
async () => await import('./ParameterInputList.vue'),
|
|
||||||
);
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FixedCollectionParameter',
|
name: 'FixedCollectionParameter',
|
||||||
components: {
|
|
||||||
ParameterInputList,
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
nodeValues: {
|
nodeValues: {
|
||||||
type: Object as PropType<Record<string, INodeParameters[]>>,
|
type: Object as PropType<Record<string, INodeParameters[]>>,
|
||||||
|
|
|
@ -65,26 +65,38 @@
|
||||||
:underline="true"
|
:underline="true"
|
||||||
color="text-dark"
|
color="text-dark"
|
||||||
/>
|
/>
|
||||||
<Suspense>
|
<Suspense v-if="!asyncLoadingError">
|
||||||
<CollectionParameter
|
<template #default>
|
||||||
v-if="parameter.type === 'collection'"
|
<CollectionParameter
|
||||||
:parameter="parameter"
|
v-if="parameter.type === 'collection'"
|
||||||
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
|
:parameter="parameter"
|
||||||
:node-values="nodeValues"
|
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
|
||||||
:path="getPath(parameter.name)"
|
:node-values="nodeValues"
|
||||||
:is-read-only="isReadOnly"
|
:path="getPath(parameter.name)"
|
||||||
@valueChanged="valueChanged"
|
:is-read-only="isReadOnly"
|
||||||
/>
|
@valueChanged="valueChanged"
|
||||||
<FixedCollectionParameter
|
/>
|
||||||
v-else-if="parameter.type === 'fixedCollection'"
|
<FixedCollectionParameter
|
||||||
:parameter="parameter"
|
v-else-if="parameter.type === 'fixedCollection'"
|
||||||
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
|
:parameter="parameter"
|
||||||
:node-values="nodeValues"
|
:values="nodeHelpers.getParameterValue(nodeValues, parameter.name, path)"
|
||||||
:path="getPath(parameter.name)"
|
:node-values="nodeValues"
|
||||||
:is-read-only="isReadOnly"
|
:path="getPath(parameter.name)"
|
||||||
@valueChanged="valueChanged"
|
:is-read-only="isReadOnly"
|
||||||
/>
|
@valueChanged="valueChanged"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #fallback>
|
||||||
|
<n8n-text size="small" class="async-notice">
|
||||||
|
<n8n-icon icon="sync-alt" size="xsmall" :spin="true" />
|
||||||
|
{{ $locale.baseText('parameterInputList.loadingFields') }}
|
||||||
|
</n8n-text>
|
||||||
|
</template>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
<n8n-text v-else size="small" color="danger" class="async-notice">
|
||||||
|
<n8n-icon icon="exclamation-triangle" size="xsmall" />
|
||||||
|
{{ $locale.baseText('parameterInputList.loadingError') }}
|
||||||
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<ResourceMapper
|
<ResourceMapper
|
||||||
v-else-if="parameter.type === 'resourceMapper'"
|
v-else-if="parameter.type === 'resourceMapper'"
|
||||||
|
@ -150,7 +162,7 @@ import type {
|
||||||
import { deepCopy } from 'n8n-workflow';
|
import { deepCopy } from 'n8n-workflow';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent, onErrorCaptured, ref } from 'vue';
|
||||||
|
|
||||||
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
|
@ -226,9 +238,31 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const nodeHelpers = useNodeHelpers();
|
const nodeHelpers = useNodeHelpers();
|
||||||
|
const asyncLoadingError = ref(false);
|
||||||
|
|
||||||
|
// This will catch errors in async components
|
||||||
|
onErrorCaptured((e, component) => {
|
||||||
|
if (
|
||||||
|
!['FixedCollectionParameter', 'CollectionParameter'].includes(
|
||||||
|
component?.$options.name as string,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
asyncLoadingError.value = true;
|
||||||
|
console.error(e);
|
||||||
|
window?.Sentry?.captureException(e, {
|
||||||
|
tags: {
|
||||||
|
asyncLoadingError: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Don't propagate the error further
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodeHelpers,
|
nodeHelpers,
|
||||||
|
asyncLoadingError,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -572,5 +606,10 @@ export default defineComponent({
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.async-notice {
|
||||||
|
display: block;
|
||||||
|
padding: var(--spacing-3xs) 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { N8nPlugin } from 'n8n-design-system';
|
||||||
import { useMessage } from '@/composables/useMessage';
|
import { useMessage } from '@/composables/useMessage';
|
||||||
import EnterpriseEdition from '@/components/EnterpriseEdition.ee.vue';
|
import EnterpriseEdition from '@/components/EnterpriseEdition.ee.vue';
|
||||||
import RBAC from '@/components/RBAC.vue';
|
import RBAC from '@/components/RBAC.vue';
|
||||||
|
import ParameterInputList from '@/components/ParameterInputList.vue';
|
||||||
|
|
||||||
export const GlobalComponentsPlugin: Plugin<{}> = {
|
export const GlobalComponentsPlugin: Plugin<{}> = {
|
||||||
install(app) {
|
install(app) {
|
||||||
|
@ -14,6 +15,7 @@ export const GlobalComponentsPlugin: Plugin<{}> = {
|
||||||
|
|
||||||
app.component('EnterpriseEdition', EnterpriseEdition);
|
app.component('EnterpriseEdition', EnterpriseEdition);
|
||||||
app.component('RBAC', RBAC);
|
app.component('RBAC', RBAC);
|
||||||
|
app.component('ParameterInputList', ParameterInputList);
|
||||||
|
|
||||||
app.use(ElementPlus);
|
app.use(ElementPlus);
|
||||||
app.use(N8nPlugin);
|
app.use(N8nPlugin);
|
||||||
|
|
|
@ -1181,6 +1181,8 @@
|
||||||
"parameterInputList.delete": "Delete",
|
"parameterInputList.delete": "Delete",
|
||||||
"parameterInputList.deleteParameter": "Delete Parameter",
|
"parameterInputList.deleteParameter": "Delete Parameter",
|
||||||
"parameterInputList.parameterOptions": "Parameter Options",
|
"parameterInputList.parameterOptions": "Parameter Options",
|
||||||
|
"parameterInputList.loadingFields": "Loading fields...",
|
||||||
|
"parameterInputList.loadingError": "Error loading fields. Refresh you page and try again.",
|
||||||
"personalizationModal.businessOwner": "Business Owner",
|
"personalizationModal.businessOwner": "Business Owner",
|
||||||
"personalizationModal.continue": "Continue",
|
"personalizationModal.continue": "Continue",
|
||||||
"personalizationModal.cicd": "CI/CD",
|
"personalizationModal.cicd": "CI/CD",
|
||||||
|
|
Loading…
Reference in a new issue