Skip to main content

Introducing Formengine - The New Formbuilder, try for FREE formengine.io.

Migration to Vue 3

Overview

Starting with Workflow Engine 20.0.0 templates are based on Vue 3 which leads to some code changes. ElementUI is replaced by Element Plus, which also affects component usage. This upgrade drops Internet Explorer support.

Vue3

Due to Vue 3 reactivity and template compiler changes, some adjustments are required in template definitions, syntax, and the event system.

Data shape

In Vue 3 the full reactive state shape should exist on first render. Each template now has a <template_name>_Data factory alongside <template_name>_Init. The data factory must return the entire state with default values. In init methods you have me.VueConfig.state — a reactive object holding state.

Before

function customforms_basicsplugin_sendemailform_Init(me) {
me.VueConfig.methods.UpdateLanguage = function () {
me.VueConfig.data = Object.assign(me.VueConfig.data, {
labels: WorkflowDesignerConstants.EditParametersFormlabel,
customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail,
ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor,
})
}

me.VueConfig.methods.UpdateLanguage();

me.VueConfig.data = Object.assign(me.VueConfig.data, {
customTitle: undefined,
readonly: false,
parameterFromProcess: {
MailServerPort: false,
MailServerSsl: false,
IsHTML: false
},
FormData: {},
Field: {},
useAuth: false
});
}

After

function customforms_basicsplugin_sendemailform_Data(me) {
return {
customTitle: undefined,
readonly: false,
parameterFromProcess: {
MailServerPort: false,
MailServerSsl: false,
IsHTML: false
},
FormData: {},
Field: {
Name: {
Name: '',
Title: '',
Comment: '',
Type: '',
IsRequired: '',
DefaultValue: null
},
State: { /* Same structure as Name field */},
MailServer: { /* Same structure as Name field */},
MailServerPort: { /* Same structure as Name field */},
MailServerFrom: { /* Same structure as Name field */},
To: { /* Same structure as Name field */},
CcList: { /* Same structure as Name field */},
BccList: { /* Same structure as Name field */},
ReplyToList: { /* Same structure as Name field */},
Subject: { /* Same structure as Name field */},
MailServerSsl: { /* Same structure as Name field */},
IsHTML: { /* Same structure as Name field */},
MailServerLogin: { /* Same structure as Name field */},
MailServerPass: { /* Same structure as Name field */},
Body: { /* Same structure as Name field */}
},
useAuth: false
}
}

function customforms_basicsplugin_sendemailform_Init(me) {
const {VueConfig} = me;
const {state: data, methods} = VueConfig;

methods.UpdateLanguage = function () {
Object.assign(data, {
labels: WorkflowDesignerConstants.EditParametersFormlabel,
customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail,
ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor,
})
}

methods.UpdateLanguage();
}

Binding to undefined

Expressions that resolve to undefined on the first render may cause warnings and break reactivity. Use conditional rendering to ensure the value exists before binding.

<el-select
v-if="!!transition?.condition?.Type"
v-model="transition.condition.Type"

Accessing possibly undefined properties

Vue 3 no longer silently ignores access errors. Use optional chaining when reading deep properties that may not exist yet.

Before

<el-form-item
:label="customLabels.LoopName"
:prop="Field.LoopName.Name"
<!-- ... -->

After

<el-form-item
:label="customLabels.LoopName"
:prop="Field?.LoopName?.Name"
<!-- ... -->

Before

<table v-if="FormData.PreExecutionImplementation.length > 0" class="WorkflowDesignerTable">

After

<table v-if="FormData.PreExecutionImplementation?.length > 0" class="WorkflowDesignerTable">

Undefined values in loops

Looping over an undefined value throws. Split v-if and v-for so Vue won't attempt to iterate when data is missing. For some elements you must move v-for to a wrapping <template>.

Before

<tr v-for="(item, index) in items" :key="index" v-if="showPredefined || item.IsPredefined!=true"
:class="dragOverIndex == index && dragOverBlock == item ? 'dragOver' : ''"
@dragend="dragend($event)" @dragover="dragover(item, index, $event)"
@dragstart="dragstart(index, $event)">

After

<template v-for="(item, index) in items" :key="index">
<tr v-if="showPredefined || item.IsPredefined !== true"
:class="dragOverIndex == index && dragOverBlock == item ? 'dragOver' : ''"
@dragend="dragend($event)" @dragover="dragover(item, index, $event)"
@dragstart="dragstart(index, $event)">

Native events

The .native modifier was removed in Vue 3. Attach events to components via the component's own events or on the root element inside the component.

  <!-- Do not use -->  
@keyup.native

Template syntax change

Inline callbacks

Inline callback now are deprecated, they create a new function on every render. Move the logic into methods.

Before

<span @click="function(){FormData.Post = parameterFromProcess.Post ? false : null; changeParameterInput('Post')}">@</span>

After

<span @click="toggleStoreResponse">@</span>
methods.togglePost = function () {
data.FormData.Post = data.parameterFromProcess.Post ? false : null;
methods.changeParameterInput('Post');
}

Element plus components

Please also check official migration guide and changelist.

Props

All non-string props must be bound as expressions.

Before

<el-input rows="5"></el-input>

After

<el-input :rows="5"></el-input>

Before

<div draggable="true"></div>

After

<div :draggable="true"></div>

The size="mini" value has been replaced with size="small" in components that support size.

Button

The type="text" prop has been replaced by link attribute.

Before

<el-button type="text" :icon="Edit" />

After

<el-button link :icon="Edit" />

Icons

Icons though predefined classes are removed. Consult documentation for complete changelist. Now we use @element-plus/icons-vue package. All icons registered with Icon prefix, ie Element Plus Refresh icon become IconRefresh and <icon-reresh /> inside WFE templates.

Before

<div class="WorkflowDesignerToolbarIcon"><i class="el-icon-circle-plus" style="color: #28ae60;"></i></div>

After

<div class="WorkflowDesignerToolbarIcon">
<icon-circle-plus style="color: #28ae60;"></icon-circle-plus>
</div>

Before

<el-button v-if="!readonly && !itemHasComment" circle icon="el-icon-s-comment" @click="showUserComment()"></el-button>

After

<el-button v-if="!readonly && !itemHasComment" circle icon="IconRefresh" @click="showUserComment()">Label</el-button>

If icon is not centered in some case you may need to wrap icon in <el-icon />.

<el-button v-if="!readonly && !itemHasComment" circle @click="showUserComment()">
<el-icon>
<icon-refresh/>
</el-icon>
</el-button>
<el-icon>
<div v-html="icons.menu"></div>
</el-icon>

Before

<i class="el-icon-info"></i>

After

<el-icon>
<icon-info-filled />
</el-icon>

Checkbox

Label now set via value prop.

Before

<el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :label="persist">{{ persist }}</el-checkbox>

After

<el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :value="persist">{{ persist }}</el-checkbox>

Value binding replace with v-model

Before

<el-checkbox :value="FormData.EnableOtherwise"></el-checkbox>

After

<el-checkbox v-model="FormData.EnableOtherwise"></el-checkbox>

RadioGroup

Label now set via value prop.

Before

<el-radio-group v-model="FormData.Alignment" size="small">
<el-radio-button label="left">

After

<el-radio-group v-model="FormData.Alignment" size="small">
<el-radio-button value="left">

Dialog

  • property before-close is removed and should not be needed.
  • visibility now controlled via v-model.
  • slot is replaced with template

Before

<el-dialog
:before-close="function() {visible = false}"
:title="title"
:visible="visible"
width="30%"
append-to-body>
<span v-html="message"></span>
<span slot="footer" class="dialog-footer">
<!-- ... -->

After

<el-dialog
:title="title"
v-model="visible"
class="Dialog"
append-to-body>
<span v-html="message"></span>
<template #footer>
<footer>
<!-- ... -->

Slot and templates

Slots must be replaced with template with id set to slot name.

Before

<span slot="footer" class="dialog-footer">
<template>
<!-- ... -->
</template>
</span>

After

<template #footer>
<span class="dialog-footer">
<!-- ... -->
</span>
</template>

Slot scope

Slot scope now must be defined with v-slot.

Before

<child-component>
<template slot-scope="scope">
{{ scope.item }}
</template>
</child-component>

After

<child-component>
<template v-slot:default="scope">
{{ scope.item }}
</template>
</child-component>

Or this way

<template v-slot:default="{ item }">
{{ item }}
</template>

Tooltip

In some case tooltip content may need an extra wrapper.

Before

<el-tooltip v-if="!settings.readonly" :content="labels.Label">
<el-popover placement="bottom" trigger="click" width="310">
<!-- ... -->
</el-popover>
</el-tooltip>

After

<el-tooltip v-if="!settings.readonly" :content="labels.Label">
<div>
<el-popover placement="bottom" trigger="click" width="310">
<!-- ... -->
</el-popover>
</div>
</el-tooltip>

Example: Weather Activity

Based on the React integration guide.

WeatherActivity.html
<h3>{{ labels.Title }}</h3>
<el-form ref="form"
:model="FormData"
class="WorkflowDesignerWindowForm"
label-position="top"
label-width="150px">
<div class="SettingsWithPadding">
<div class="el-form--inline el-form--label-top">
<el-form-item :label="labels.Name" :rules="activityNameRules()" class="el-form-item" prop="Name" style="flex-grow: 1;">
<el-input v-model="FormData.Name" :placeholder="labels.Name" :readonly="readonly" @input="nameOnChange"></el-input>
</el-form-item>
<el-form-item :label="labels.State" class="el-form-item" prop="State" style="flex-grow: 1;">
<el-input v-model="FormData.State" :placeholder="labels.State" :readonly="readonly"></el-input>
</el-form-item>
</div>
<el-form-item size="small">
<el-checkbox v-model="FormData.IsInitial" :disabled="readonly" :label="labels.IsInitial" name="type"
@change="onInitialChange"></el-checkbox>
<el-checkbox v-model="FormData.IsFinal" :disabled="readonly" :label="labels.IsFinal" name="type"
@change="onFinalChange"></el-checkbox>
<el-checkbox v-model="FormData.IsForSetState" :disabled="readonly" :label="labels.IsForSetState" name="type"></el-checkbox>
<el-checkbox v-model="FormData.IsAutoSchemeUpdate" :disabled="readonly" :label="labels.IsAutoSchemeUpdate" name="type"></el-checkbox>
<el-checkbox v-if="expertMode" v-model="disableAllPersist" :disabled="readonly" :indeterminate="isIndeterminate"
:label="labels.DisablePersist" @change="handleCheckAllPersistsChange"></el-checkbox>
</el-form-item>

<el-checkbox-group v-if="expertMode && (disableAllPersist || isIndeterminate)" v-model="checkedPersists"
@change="handleCheckedPersistsChange">
<el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :label="persist" :value="persist">{{ persist }}</el-checkbox>
</el-checkbox-group>
</div>

<div style="margin-bottom: 10px;">
<h4 class="WorkflowDesignerTitleWithCreate Underline">
{{labels.Implementation}}
<a v-if="!readonly" @click="addActionRow(FormData.Implementation)">{{ButtonTextCreate}}</a>
</h4>
<table v-if="FormData.Implementation.length > 0" class="WorkflowDesignerTable">
<tr>
<th></th>
<th>{{labels.ImpAction}}</th>
<th>{{labels.ImpActionParameter}}</th>
</tr>
<tr v-for="(imp, index) in FormData.Implementation" :key="index" :class="dragOverIndex == index && dragOverBlock == imp ? 'dragOver' : ''"
@dragend="dragend($event)"
@dragover="dragover(imp, index, $event)"
@dragstart="dragstart(index, $event, FormData.Implementation)">

<td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
<div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
</td >
<td >
<el-select
v-model="imp.ActionName"
:class="validateField(notEmpty, imp.ActionName) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.ImpAction"
:title="validateField(notEmpty, imp.ActionName)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in actions" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td>
<el-autocomplete v-model="imp.ActionParameter" :fetch-suggestions="querySearch" :placeholder="labels.ImpActionParameter" :readonly="readonly"
clearable style="width: 100%;" @focus="setCurrentItem(imp)"></el-autocomplete>
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
<el-button-group>
<el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == imp ? 'is-active' : '')"
@click="showjson('ActionParameter', imp)"></el-button>
<el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.Implementation, index)"></el-button>
</el-button-group>
</td>
</tr>
</table>
</div>
<div v-if="expertMode" style="margin-bottom: 10px;">
<h4 class="WorkflowDesignerTitleWithCreate Underline">
{{labels.PreExecutionImplementation}}
<a v-if="!readonly" @click="addActionRow(FormData.PreExecutionImplementation)">{{ButtonTextCreate}}</a>
</h4>
<table v-if="FormData.PreExecutionImplementation.length > 0" class="WorkflowDesignerTable">
<tr>
<th></th>
<th>{{labels.ImpAction}}</th>
<th>{{labels.ImpActionParameter}}</th>
</tr>
<tr v-for="(imp, index) in FormData.PreExecutionImplementation" :key="index"
:class="dragOverIndex == index && dragOverBlock == imp ? 'dragOver' : ''"
@dragend="dragend($event)"
@dragover="dragover(imp, index, $event)"
@dragstart="dragstart(index, $event, FormData.PreExecutionImplementation)">
<td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
<div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
</td>
<td>
<el-select
v-model="imp.ActionName"
:class="validateField(notEmpty, imp.ActionName) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.ImpAction"
:title="validateField(notEmpty, imp.ActionName)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in actions" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td>
<el-autocomplete v-model="imp.ActionParameter" :fetch-suggestions="querySearch" :placeholder="labels.ImpActionParameter" :readonly="readonly"
clearable style="width: 100%;" @focus="setCurrentItem(imp)"></el-autocomplete>
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
<el-button-group>
<el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == imp ? 'is-active' : '')"
@click="showjson('ActionParameter', imp)"></el-button>
<el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.PreExecutionImplementation, index)"></el-button>
</el-button-group>
</td>
</tr>
</table>
</div>
<div v-if="expertMode" style="margin-bottom: 10px;">
<h4 class="WorkflowDesignerTitleWithCreate Underline">
{{labels.Annotations}}
<a v-if="!readonly" @click="addAnnotationRow(FormData.Annotations)">{{ButtonTextCreate}}</a>
</h4>
<table v-if="FormData.Annotations.length > 0" class="WorkflowDesignerTable">
<tr>
<th></th>
<th>{{ labels.AnnotationName }}</th>
<th>{{ labels.AnnotationValue }}</th>
</tr>
<tr v-for="(annotation, index) in FormData.Annotations" :key="index"
:class="dragOverIndex == index && dragOverBlock == annotation ? 'dragOver' : ''"
@dragend="dragend($event)"
@dragover="dragover(annotation, index, $event)"
@dragstart="dragstart(index, $event, FormData.Annotations)">
<td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
<div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
</td>
<td>
<el-form-item :inline-message="true" :prop="'Annotations.' + index + '.Name'" :rules="annotationNameRules()" class="el-form-item"
style="flex-grow: 1;">
<el-input
v-model="annotation.Name"
:class="validateField(notEmpty, annotation.Name) ? 'WorkflowDesignerInputError' : ''"
:placeholder="labels.AnnotationName"
:title="validateField(notEmpty, annotation.Name)"
clearable >
</el-input>
</el-form-item>
</td>
<td >
<el-input
v-model="annotation.JsonValue"
:placeholder="labels.AnnotationValue"
:readonly="readonly"
clearable>
</el-input>
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
<el-button-group>
<el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == annotation ? 'is-active' : '')"
@click="showjson('JsonValue', annotation)"></el-button>
<el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.Annotations, index)"></el-button>
</el-button-group>
</td>
</tr>
</table>
</div>
<div v-if="expertMode" style="margin-bottom: 10px;">
<h4 style="padding-bottom: 1px;border-bottom: 1px solid rgba(34,36,38,.15);">{{ labels.TimeoutsLabel }}</h4>
<table class="WorkflowDesignerTable">
<tr>
<th>
<el-tooltip :content="labels.ExecutionTimeoutWarning" placement="top-start">
<div>
{{ labels.ExecutionTimeoutLabel }}<i class="el-icon-warning"
style="display: contents; color: #FF3300; opacity: 0.6; margin-left: 3px;"></i>
</div>
</el-tooltip>
</th>
<th>{{ labels.TypeLabel }}</th>
<th v-if="FormData.ExecutionTimeout.Type == 'SetActivity'">{{ labels.ActivityLabel }}</th>
<th v-if="FormData.ExecutionTimeout.Type == 'SetState'">{{ labels.StateLabel }}</th>
<th v-if="FormData.ExecutionTimeout.Type == 'Retry'">{{ labels.RetryCountLabel }}</th>
</tr>
<tr>
<td :label="labels.ExecutionTimeoutLabel" :readonly="readonly">
<el-input v-model="FormData.ExecutionTimeout.Timer" :class="(FormData.ExecutionTimeout.Type ? validateField(notEmpty, FormData.ExecutionTimeout.Timer) : false) ? 'WorkflowDesignerInputError' : ''"
:placeholder="labels.ExecutionTimeoutLabel"
:readonly="readonly"
:title="FormData.ExecutionTimeout.Type ? validateField(notEmpty, FormData.ExecutionTimeout.Timer):''"
clearable></el-input>
</td>
<td >
<el-select
v-model="FormData.ExecutionTimeout.Type"
:class="(FormData.ExecutionTimeout.Timer ? validateField(notEmpty, FormData.ExecutionTimeout.Type) : false) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.TypeLabel"
:title="FormData.ExecutionTimeout.Timer ? validateField(notEmpty, FormData.ExecutionTimeout.Type):''"
clearable
filterable
style="width: 100%;"
@change="delete FormData.ExecutionTimeout.NameForSet">
<el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
<el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
<el-option key="Retry" :label="labels.RetryLabel" value="Retry"></el-option>
</el-select>
</td>
<td v-if="FormData.ExecutionTimeout.Type == 'SetActivity'" >
<el-select
v-model="FormData.ExecutionTimeout.NameForSet"
:class="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.ActivityLabel"
:title="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="FormData.ExecutionTimeout.Type == 'SetState'">
<el-select
v-model="FormData.ExecutionTimeout.NameForSet"
:class="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.StateLabel"
:title="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in states" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="FormData.ExecutionTimeout.Type == 'Retry'">
<el-input-number
v-model="FormData.ExecutionTimeout.RetryCount" :class="validateField(notEmpty, FormData.ExecutionTimeout.RetryCount) ? 'WorkflowDesignerInputError' : ''"
:placeholder="labels.RetryCountLabel"
:readonly="readonly"
:title="validateField(notEmpty, FormData.ExecutionTimeout.RetryCount)" clearable controls-position="right" style="width: 100%;"></el-input-number>
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButton">
<el-button class="WorkflowDesignerTableDeleteButton" @click="FormData.ExecutionTimeout = {RetryCount:'1'}"></el-button>
</td>
</tr>
</table>
<table class="WorkflowDesignerTable">
<tr>
<th>{{labels.IdleTimeoutLabel}}</th>
<th>{{labels.TypeLabel}}</th>
<th v-if="FormData.IdleTimeout.Type == 'SetActivity'">{{labels.ActivityLabel}}</th>
<th v-if="FormData.IdleTimeout.Type == 'SetState'">{{labels.StateLabel}}</th>
</tr>
<tr>
<td :readonly="readonly">
<el-input v-model="FormData.IdleTimeout.Timer"
:class="(FormData.IdleTimeout.Type ? validateField(notEmpty, FormData.IdleTimeout.Timer) : false) ? 'WorkflowDesignerInputError' : ''"
:placeholder="labels.IdleTimeoutLabel"
:readonly="readonly"
:title="FormData.IdleTimeout.Type ? validateField(notEmpty, FormData.IdleTimeout.Timer):''"
clearable></el-input>
</td>
<td :readonly="readonly">
<el-select
v-model="FormData.IdleTimeout.Type"
:class="(FormData.IdleTimeout.Timer ? validateField(notEmpty, FormData.IdleTimeout.Type) : false) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.TypeLabel"
:title="FormData.IdleTimeout.Timer ? validateField(notEmpty, FormData.IdleTimeout.Type):''"
clearable
filterable
style="width: 100%;"
@change="delete FormData.IdleTimeout.NameForSet">
<el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
<el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
</el-select>
</td>
<td v-if="FormData.IdleTimeout.Type == 'SetActivity'">
<el-select
v-model="FormData.IdleTimeout.NameForSet"
:class="validateField(notEmpty, FormData.IdleTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.ActivityLabel"
:title="validateField(notEmpty, FormData.IdleTimeout.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="FormData.IdleTimeout.Type == 'SetState'">
<el-select
v-model="FormData.IdleTimeout.NameForSet"
:class="validateField(notEmpty, FormData.IdleTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.StateLabel"
:title="validateField(notEmpty, FormData.IdleTimeout.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in states" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButton">
<el-button class="WorkflowDesignerTableDeleteButton" @click="FormData.IdleTimeout = {RetryCount:'1'}"></el-button>
</td>
</tr>
</table>
</div>
<div v-if="expertMode" style="margin-bottom: 10px;">
<h4 class="WorkflowDesignerTitleWithCreate Underline">
{{labels.ExceptionsHandling}}
<a v-if="!readonly" @click="addRow(FormData.ExceptionsHandlers)">{{ButtonTextCreate}}</a>
</h4>
<table v-if="FormData.ExceptionsHandlers.length > 0" class="WorkflowDesignerTable">
<tr>
<th></th>
<th>{{labels.Exceptions}}</th>
<th>{{labels.TypeLabel}}</th>
<th>{{labels.ImpAction}}</th>
</tr>
<tr v-for="(imp, index) in FormData.ExceptionsHandlers" :key="index"
:class="dragOverIndex == index && dragOverBlock == imp ? 'dragOver' : ''"
@dragend="dragend($event)"
@dragover="dragover(imp, index, $event)"
@dragstart="dragstart(index, $event, FormData.ExceptionsHandlers)">
<td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
<div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
</td >
<td>
<el-select
v-model="imp.Exceptions"
:class="validateField(notEmpty, imp.Exceptions) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.Exceptions"
:title="validateField(notEmpty, imp.Exceptions)"
allow-create
clearable
filterable
filterable
multiple
style="width: 100%;">
<el-option key="AllExceptions" :label="labels.AllExceptions" value="*"></el-option>
</el-select>
</td>
<td >
<el-select
v-model="imp.Type"
:class="validateField(notEmpty, imp.Type) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.TypeLabel"
:title="validateField(notEmpty, imp.Type)"
clearable
filterable
style="width: 100%;"
@change="delete imp.NameForSet">
<el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
<el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
<el-option key="Retry" :label="labels.RetryLabel" value="Retry"></el-option>
<el-option key="Ignore" :label="labels.IgnoreLabel" value="Ignore"></el-option>
</el-select>
</td>
<td v-if="imp.Type == 'SetActivity'"
:readonly="readonly">
<el-select
v-model="imp.NameForSet"
:class="validateField(notEmpty, imp.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.ActivityLabel"
:title="validateField(notEmpty, imp.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="imp.Type == 'SetState'" >
<el-select
v-model="imp.NameForSet"
:class="validateField(notEmpty, imp.NameForSet) ? 'WorkflowDesignerInputError' : ''"
:disabled="readonly"
:placeholder="labels.StateLabel"
:title="validateField(notEmpty, imp.NameForSet)"
clearable
filterable
style="width: 100%;">
<el-option v-for="item in states" :key="item" :label="item" :value="item"></el-option>
</el-select>
</td>
<td v-if="imp.Type == 'Retry'">
<el-input-number v-model="imp.RetryCount" :class="validateField(notEmpty, imp.RetryCount) ? 'WorkflowDesignerInputError' : ''"
:placeholder="labels.RetryCountLabel"
:readonly="readonly"
:title="validateField(notEmpty, imp.RetryCount)"
clearable
controls-position="right" style="width: 100%;"></el-input-number>
</td>
<td v-if="imp.Type == 'Ignore' || !imp.Type">
</td>
<td v-if="!readonly" class="WorkflowDesignerTableEditButton">
<el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.ExceptionsHandlers, index)"></el-button>
</td>
</tr>
</table>
</div>
<div style="margin-bottom: 10px;">
<el-button v-if="!readonly && !itemHasComment" circle icon="IconComment" @click="showUserComment()"></el-button>
<h4 v-if="itemHasComment" style="padding-bottom: 1px;border-bottom: 1px solid rgba(34,36,38,.15);">{{ labels.UserComment }}</h4>
<el-input
v-if="itemHasComment"
v-model="FormData.UserComment"
:placeholder="labels.UserComment"
:rows="5"
type="textarea"
>
</el-input>
</div>
</el-form>
<div class="WorkflowDesignerButtons">
<el-button link @click="expertMode = !expertMode">{{ expertMode ? SwitchToDefaultMode : SwitchToExpertMode }}</el-button>
<el-button v-if="!readonly" type="primary" @click="onSave">{{ ButtonTextSave }}</el-button>
<el-button @click="onClose">{{ ButtonTextCancel }}</el-button>
</div>
<script type="application/javascript">
function weatherActivity_Data(me) {
return {
readonly: false,
labels: WorkflowDesignerConstants.ActivityFormLabel,
ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
ButtonTextCreate: WorkflowDesignerConstants.ButtonTextCreate,
ButtonTextDelete: WorkflowDesignerConstants.ButtonTextDelete,
SwitchToDefaultMode: WorkflowDesignerConstants.SwitchToDefaultMode,
SwitchToExpertMode: WorkflowDesignerConstants.SwitchToExpertMode,
expertMode: false,
actions: [],
activities: [],
states: [],
editItem: null,
itemHasComment: false,
FormData: {
Implementation: [],
PreExecutionImplementation: [],
Annotations: [],
ExceptionsHandlers: [],
},
actionsStore: me.graph.data.CodeActions,
disableAllPersist: false,
isIndeterminate: false,
persists: [],
checkedPersists: []
}
}

function weatherActivity_Init(me) {

me.VueConfig.watch = {
actionsStore:function(val) {
me.VueConfig.state.actions = me.graph.getActionNames();
},
}

me.VueConfig.methods.onUpdate = function(item){
debugger
var formdata = me.VueConfig.state.FormData;
formdata.Name = item.Name;
formdata.ExecutionTimeout = item.ExecutionTimeout ? WorkflowDesignerCommon.clone(item.ExecutionTimeout) : {RetryCount: '1'};
formdata.IdleTimeout = item.IdleTimeout ? WorkflowDesignerCommon.clone(item.IdleTimeout) : {RetryCount: '1'};
// formdata.ExceptionsHandler = item.ExceptionsHandler ? WorkflowDesignerCommon.clone(item.ExceptionsHandler) : {RetryCount:"1", Exceptions:[]};
formdata.ExceptionsHandlers = Array.isArray(item.ExceptionsHandlers) ? WorkflowDesignerCommon.clone(item.ExceptionsHandlers) : [];
formdata.State = item.State;
formdata.IsInitial = item.IsInitial;
formdata.IsFinal = item.IsFinal;
formdata.IsForSetState = item.IsForSetState;
formdata.IsAutoSchemeUpdate = item.IsAutoSchemeUpdate;
formdata.DisablePersistState = item.DisablePersistState;
formdata.DisablePersistTransitionHistory = item.DisablePersistTransitionHistory;
formdata.DisablePersistParameters = item.DisablePersistParameters;
formdata.UserComment = item.UserComment;
formdata.Implementation = Array.isArray(item.Implementation) ? WorkflowDesignerCommon.clone(item.Implementation) : [];
formdata.PreExecutionImplementation = Array.isArray(item.PreExecutionImplementation) ? WorkflowDesignerCommon.clone(item.PreExecutionImplementation) : [];
formdata.Annotations = Array.isArray(item.Annotations) ? WorkflowDesignerCommon.clone(item.Annotations) : [];

me.linkItem = item;
me.VueConfig.state.originalItem = WorkflowDesignerCommon.clone(formdata);
me.VueConfig.state.prevName = item.Name;
me.VueConfig.state.readonly = me.graph.Settings.readonly;
me.VueConfig.state.actions = me.graph.getActionNames();
me.VueConfig.state.itemHasComment = formdata.UserComment != null && formdata.UserComment.length > 0;

const {activities, states} = me.VueConfig.state;
me.graph.data.Activities.forEach(({Name, State}) => {
if (item.Name === Name) return;

if (!activities.includes(Name)) activities.push(Name);
if (State && !states.includes(State)) states.push(State);
});

activities.sort();
states.sort();

me.VueConfig.state.disableAllPersist = formdata.DisablePersistState && formdata.DisablePersistTransitionHistory && formdata.DisablePersistParameters;
me.VueConfig.state.isIndeterminate = formdata.DisablePersistState || formdata.DisablePersistTransitionHistory || formdata.DisablePersistParameters;
me.VueConfig.state.persists = [
me.VueConfig.state.labels.DisablePersists.DisablePersistState,
me.VueConfig.state.labels.DisablePersists.DisablePersistParameters,
me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory
];
if (formdata.DisablePersistState != undefined && formdata.DisablePersistState) {
me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistState);
}
if (formdata.DisablePersistParameters != undefined && formdata.DisablePersistParameters) {
me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistParameters);
}
if (formdata.DisablePersistTransitionHistory != undefined && formdata.DisablePersistTransitionHistory) {
me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory);
}

var objectCorrect = me.VueConfig.methods.objectCorrect;

if ((formdata.PreExecutionImplementation.length > 0)
|| (formdata.Annotations.length > 0)
|| objectCorrect(formdata.ExecutionTimeout.Type)
|| objectCorrect(formdata.IdleTimeout.Type)
|| formdata.ExceptionsHandlers.length > 0
|| me.VueConfig.state.disableAllPersist
|| me.VueConfig.state.isIndeterminate) {
me.VueConfig.state.expertMode = true;
}
};


me.VueConfig.methods.activityNameRules = function(){
var res = me.requiredRule();

var validator = function(rule, value, callback){
var hasEqualNames = me.graph.data.Activities.find(function (a) {
return a != me.linkItem && a.Name == value;
});

hasEqualNames ? callback(new Error(rule.message)) : callback();
};
res.push({ validator: validator, message: WorkflowDesignerConstants.FieldMustBeUnique});
return res;
}

me.VueConfig.methods.validateField = function (rules, value) {

var value = arguments[arguments.length - 1];

for (var i = 0; i < arguments.length - 1; i++) {
var error = arguments[i](value);
if (error)
return error;
}


};

me.VueConfig.methods.notEmpty = function (value) {
var isValid = true;
if(!me.VueConfig.methods.objectCorrect(value))
isValid = false;


if (Object.prototype.toString.call(value) === '[object Array]') {
if (value.length < 1)
isValid = false;
}

if (!isValid)
return WorkflowDesignerConstants.FieldIsRequired;
}

me.VueConfig.methods.nameOnChange = function (value) {
var formdata = me.VueConfig.state.FormData;
if (formdata.State == me.VueConfig.state.prevName) {
formdata.State = value;
}
me.VueConfig.state.prevName = value;
};

me.VueConfig.methods.onInitialChange = function(){
var formdata = me.VueConfig.state.FormData;
formdata.IsFinal = formdata.IsInitial ? false : formdata.IsFinal;
};

me.VueConfig.methods.onFinalChange = function(){
var formdata = me.VueConfig.state.FormData;
formdata.IsInitial = formdata.IsFinal ? false : formdata.IsInitial;
};

me.VueConfig.methods.handleCheckAllPersistsChange = function (val) {
me.VueConfig.state.checkedPersists = val ? me.VueConfig.state.persists : [];
me.VueConfig.state.isIndeterminate = false;
};

me.VueConfig.methods.handleCheckedPersistsChange = function (value) {
let checkedCount = value.length;
me.VueConfig.state.disableAllPersist = checkedCount === me.VueConfig.state.persists.length;
me.VueConfig.state.isIndeterminate = checkedCount > 0 && checkedCount < me.VueConfig.state.persists.length;
}

me.VueConfig.methods.setCurrentItem = function(item) {
this.currentItem = item;
};
me.VueConfig.methods.querySearch = function(queryString, cb) {
if(me.VueConfig.state.readonly)
return cb([]);

var res = me.graph.getAutoCompleteSuggestions2('actionparameter', this.currentItem.ActionName, queryString);
cb(res);
};

me.VueConfig.methods.validate = function () {
var validateFunc = me.VueConfig.methods.validateField;
var data = me.VueConfig.state.FormData;
var notEmptyRule = me.VueConfig.methods.notEmpty;
for (var i = 0; i < data.Implementation.length; i++) {
var item = data.Implementation[i];
if (validateFunc(notEmptyRule, item.ActionName)) {
return false;
}

}

for (var i = 0; i < data.PreExecutionImplementation.length; i++) {
var item = data.PreExecutionImplementation[i];
if (validateFunc(notEmptyRule, item.ActionName)) {
return false;
}
}

for (var i = 0; i < data.Annotations.length; i++) {
var item = data.Annotations[i];
if (validateFunc(notEmptyRule, item.Name)) {
return false;
}
}

if (data.ExecutionTimeout.Type) {
if (validateFunc(notEmptyRule, data.ExecutionTimeout.Timer)) {
return false;
}
}
if (data.ExecutionTimeout.Timer) {
if (validateFunc(notEmptyRule, data.ExecutionTimeout.Type)) {
return false;
}
}

if (data.ExecutionTimeout.Type == 'SetActivity' || data.ExecutionTimeout.Type == 'SetState') {
if (validateFunc(notEmptyRule, data.ExecutionTimeout.NameForSet)) {
return false;
}
}

if (data.ExecutionTimeout.Type == 'Retry') {
if (validateFunc(notEmptyRule, data.ExecutionTimeout.RetryCount)) {
return false;
}
}

if (data.IdleTimeout.Type) {
if (validateFunc(notEmptyRule, data.IdleTimeout.Timer)) {
return false;
}
}

if (data.IdleTimeout.Timer) {
if (validateFunc(notEmptyRule, data.IdleTimeout.Type)) {
return false;
}
}

if (typeof data.IdleTimeout.Type != 'undefined') {
if (validateFunc(notEmptyRule, data.IdleTimeout.NameForSet)) {
return false;
}
}

for (var i = 0; i < data.ExceptionsHandlers.length; i++) {
var item = data.ExceptionsHandlers[i];
if (validateFunc(notEmptyRule, item.Exceptions) ||
validateFunc(notEmptyRule, item.Type)) {
return false;
}
if (item.Type == 'SetActivity' || item.Type == 'SetState') {
if (validateFunc(notEmptyRule, item.NameForSet)) {
return false;
}
}

if (item.Type == 'Retry') {
if (validateFunc(notEmptyRule, item.RetryCount)) {
return false;
}
}
}

return true;
};

me.VueConfig.methods.showUserComment = function () {
me.VueConfig.state.itemHasComment = true;
}

me.VueConfig.methods.addRow = function (items) {
items.push({});
};

me.VueConfig.methods.addActionRow = function (items) {
items.push({ActionParameter: ''});
};

me.VueConfig.methods.addAnnotationRow = function (items) {
items.push({JsonValue: null});
};

me.VueConfig.methods.removeRow = function (items, index) {
items.splice(index, 1);
};

me.VueConfig.methods.showjson = function (name, item) {
me.VueConfig.state.editItem = item;
me.editItem = item;

var onSuccess = function (value) {
if (me.editItem) {
me.editItem[name] = value;
me.VueConfig.state.editItem = undefined;
delete me.editItem;
}
};

var onClose = function (value) {
me.VueConfig.state.editItem = undefined;
};

var params = {
name: item['ActionName'],
type: ['Action']
};
me.VueConfig.state.jsonform = me.showjson(item[name], params, onSuccess, onClose);
};

me.VueConfig.methods.onSave = function () {
me.VueConfig.state.FormData.DisablePersistState = false;
me.VueConfig.state.FormData.DisablePersistParameters = false;
me.VueConfig.state.FormData.DisablePersistTransitionHistory = false;

const persists = me.VueConfig.state.checkedPersists;
for (var i = 0; i < persists.length; i++) {
switch (persists[i]) {
case me.VueConfig.state.labels.DisablePersists.DisablePersistState:
me.VueConfig.state.FormData.DisablePersistState = true;
break;
case me.VueConfig.state.labels.DisablePersists.DisablePersistParameters:
me.VueConfig.state.FormData.DisablePersistParameters = true;
break;
case me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory:
me.VueConfig.state.FormData.DisablePersistTransitionHistory = true;
break;
}
}

if (this.$refs && WorkflowDesignerCommon.validateForms(this.$refs) && me.VueConfig.methods.validate()) {
me.onSuccess(me.VueConfig.state.FormData);
me.onClose(true);
}
};

me.VueConfig.methods.onClose = function () {
if (me.VueConfig.state.readonly) {
me.onClose(true);
return;
}

var originalItem = me.VueConfig.state.originalItem;
var item = me.VueConfig.state.FormData;

if (WorkflowDesignerCommon.deepCompare(originalItem, item)) {
me.onClose(true);
} else {
me.showConfirm();
return false;
}
};

me.VueConfig.methods.onCloseSave = function () {
me.onClose(true);
};

me.showConfirm = function () {
me.VueConfig.methods.showConfirm({
title: WorkflowDesignerConstants.DialogConfirmText,
message: WorkflowDesignerConstants.CloseWithoutSaving,
onSuccess: function () {
me.VueConfig.methods.onCloseSave();
}
});
}
}
</script>