diff --git a/packages/editor-ui/src/components/AssignmentCollection/AssignmentCollection.vue b/packages/editor-ui/src/components/AssignmentCollection/AssignmentCollection.vue index d1828c3647..213269ab2f 100644 --- a/packages/editor-ui/src/components/AssignmentCollection/AssignmentCollection.vue +++ b/packages/editor-ui/src/components/AssignmentCollection/AssignmentCollection.vue @@ -153,7 +153,7 @@ function optionSelected(action: string) { { :is-read-only="readOnly" :parameter="leftParameter" :value="condition.leftValue" - :path="`${path}.left`" + :path="`${path}.leftValue`" :class="[$style.input, $style.inputLeft]" data-test-id="filter-condition-left" @update="onLeftValueChange" @@ -212,7 +212,7 @@ const onBlur = (): void => { :options-position="breakpoint === 'default' ? 'top' : 'bottom'" :parameter="rightParameter" :value="condition.rightValue" - :path="`${path}.right`" + :path="`${path}.rightValue`" :class="[$style.input, $style.inputRight]" data-test-id="filter-condition-right" @update="onRightValueChange" diff --git a/packages/editor-ui/src/components/FilterConditions/FilterConditions.vue b/packages/editor-ui/src/components/FilterConditions/FilterConditions.vue index b891b0c3bc..241f656c88 100644 --- a/packages/editor-ui/src/components/FilterConditions/FilterConditions.vue +++ b/packages/editor-ui/src/components/FilterConditions/FilterConditions.vue @@ -195,7 +195,7 @@ function getIssues(index: number): string[] { :read-only="readOnly" :can-remove="index !== 0 || state.paramValue.conditions.length > 1" :can-drag="index !== 0 || state.paramValue.conditions.length > 1" - :path="`${path}.${index}`" + :path="`${path}.conditions.${index}`" :issues="getIssues(index)" :class="$style.condition" @update="(value) => onConditionUpdate(index, value)" diff --git a/packages/editor-ui/src/components/ParameterInput.vue b/packages/editor-ui/src/components/ParameterInput.vue index 4183ab562e..4c92c82615 100644 --- a/packages/editor-ui/src/components/ParameterInput.vue +++ b/packages/editor-ui/src/components/ParameterInput.vue @@ -66,6 +66,7 @@ import type { EventBus } from 'n8n-design-system/utils'; import { createEventBus } from 'n8n-design-system/utils'; import { useRouter } from 'vue-router'; import { useElementSize } from '@vueuse/core'; +import { completeExpressionSyntax, isStringWithExpressionSyntax } from '@/utils/expressions'; type Picker = { $emit: (arg0: string, arg1: Date) => void }; @@ -813,16 +814,25 @@ function valueChanged(value: NodeParameterValueType | {} | Date) { if (remoteParameterOptionsLoading.value) { return; } - // Only update the value if it has changed + const oldValue = get(node.value, props.path); + if (oldValue !== undefined && oldValue === value) { + // Only update the value if it has changed return; } + if (!oldValue && oldValue !== undefined && isStringWithExpressionSyntax(value)) { + // if empty old value and updated value has an expression, add '=' prefix to switch to expression mode + value = '=' + value; + } + if (props.parameter.name === 'nodeCredentialType') { activeCredentialType.value = value as string; } + value = completeExpressionSyntax(value); + if (value instanceof Date) { value = value.toISOString(); } diff --git a/packages/editor-ui/src/utils/expressions.test.ts b/packages/editor-ui/src/utils/expressions.test.ts index b2b11728c5..1ae1a47f5e 100644 --- a/packages/editor-ui/src/utils/expressions.test.ts +++ b/packages/editor-ui/src/utils/expressions.test.ts @@ -1,5 +1,11 @@ import { ExpressionError } from 'n8n-workflow'; -import { removeExpressionPrefix, stringifyExpressionResult, unwrapExpression } from './expressions'; +import { + completeExpressionSyntax, + isStringWithExpressionSyntax, + removeExpressionPrefix, + stringifyExpressionResult, + unwrapExpression, +} from './expressions'; describe('Utils: Expressions', () => { describe('stringifyExpressionResult()', () => { @@ -53,4 +59,44 @@ describe('Utils: Expressions', () => { expect(removeExpressionPrefix(input)).toBe(output); }); }); + + describe('completeExpressionSyntax', () => { + it('should complete expressions with "{{ " at the end', () => { + expect(completeExpressionSyntax('test {{ ')).toBe('=test {{ }}'); + }); + + it('should complete expressions with "{{$" at the end', () => { + expect(completeExpressionSyntax('test {{$')).toBe('=test {{ $ }}'); + }); + + it('should not modify already valid expressions', () => { + expect(completeExpressionSyntax('=valid expression')).toBe('=valid expression'); + }); + + it('should return non-string values unchanged', () => { + expect(completeExpressionSyntax(123)).toBe(123); + expect(completeExpressionSyntax(true)).toBe(true); + expect(completeExpressionSyntax(null)).toBe(null); + }); + }); + + describe('isStringWithExpressionSyntax', () => { + it('should return true for strings with expression syntax', () => { + expect(isStringWithExpressionSyntax('test {{ value }}')).toBe(true); + }); + + it('should return false for strings without expression syntax', () => { + expect(isStringWithExpressionSyntax('just a string')).toBe(false); + }); + + it('should return false for strings starting with "="', () => { + expect(isStringWithExpressionSyntax('=expression {{ value }}')).toBe(false); + }); + + it('should return false for non-string values', () => { + expect(isStringWithExpressionSyntax(123)).toBe(false); + expect(isStringWithExpressionSyntax(true)).toBe(false); + expect(isStringWithExpressionSyntax(null)).toBe(false); + }); + }); }); diff --git a/packages/editor-ui/src/utils/expressions.ts b/packages/editor-ui/src/utils/expressions.ts index d572006ad5..155b83d9da 100644 --- a/packages/editor-ui/src/utils/expressions.ts +++ b/packages/editor-ui/src/utils/expressions.ts @@ -139,3 +139,21 @@ export const stringifyExpressionResult = ( return typeof result.result === 'string' ? result.result : String(result.result); }; + +export const completeExpressionSyntax = (value: T) => { + if (typeof value === 'string' && !value.startsWith('=')) { + if (value.endsWith('{{ ')) return '=' + value + ' }}'; + if (value.endsWith('{{$')) return '=' + value.slice(0, -1) + ' $ }}'; + } + + return value; +}; + +export const isStringWithExpressionSyntax = (value: T): boolean => { + return ( + typeof value === 'string' && + !value.startsWith('=') && + value.includes('{{') && + value.includes('}}') + ); +};