mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-50868 Fix some visibility conditions scenarios with form variables (#12206)
* AAE-50868 Fix some visibility conditions senarios with form variables * form 0 check updates * copilot review suggestions
This commit is contained in:
@@ -20,4 +20,5 @@ export interface FormVariableModel {
|
|||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
|
runtimeSet?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,6 +600,33 @@ describe('FormModel', () => {
|
|||||||
const missing = form.getProcessVariableValue('missing');
|
const missing = form.getProcessVariableValue('missing');
|
||||||
expect(missing).toBeUndefined();
|
expect(missing).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return zero process variable value instead of the form default', () => {
|
||||||
|
const formWithZero = new FormModel({
|
||||||
|
variables: [{ id: 'amount-var', name: 'amount', type: 'integer', value: 99 }],
|
||||||
|
processVariables: [{ name: 'variables.amount', value: 0, type: 'integer' }]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(formWithZero.getProcessVariableValue('amount')).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false process variable value instead of the form default', () => {
|
||||||
|
const formWithFalse = new FormModel({
|
||||||
|
variables: [{ id: 'flag-var', name: 'flag', type: 'boolean', value: true }],
|
||||||
|
processVariables: [{ name: 'variables.flag', value: false, type: 'boolean' }]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(formWithFalse.getProcessVariableValue('flag')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty string process variable value instead of the form default', () => {
|
||||||
|
const formWithEmpty = new FormModel({
|
||||||
|
variables: [{ id: 'text-var', name: 'text', type: 'string', value: 'default' }],
|
||||||
|
processVariables: [{ name: 'variables.text', value: '', type: 'string' }]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(formWithEmpty.getProcessVariableValue('text')).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('add values not present', () => {
|
describe('add values not present', () => {
|
||||||
|
|||||||
@@ -307,22 +307,17 @@ export class FormModel implements ProcessFormModel {
|
|||||||
* @returns process variable value
|
* @returns process variable value
|
||||||
*/
|
*/
|
||||||
getProcessVariableValue(name: string): any {
|
getProcessVariableValue(name: string): any {
|
||||||
let value;
|
|
||||||
if (this.processVariables?.length) {
|
if (this.processVariables?.length) {
|
||||||
const names = [`variables.${name}`, name];
|
const names = [`variables.${name}`, name];
|
||||||
|
|
||||||
const processVariable = this.processVariables.find((entry) => names.includes(entry.name));
|
const processVariable = this.processVariables.find((entry) => names.includes(entry.name));
|
||||||
|
|
||||||
if (processVariable) {
|
if (processVariable) {
|
||||||
value = this.parseValue(processVariable.type, processVariable.value);
|
return this.parseValue(processVariable.type, processVariable.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
return this.getDefaultFormVariableValue(name);
|
||||||
value = this.getDefaultFormVariableValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected parseValue(type: string, value: any): any {
|
protected parseValue(type: string, value: any): any {
|
||||||
@@ -535,9 +530,20 @@ export class FormModel implements ProcessFormModel {
|
|||||||
const variable = this.getFormVariable(variableId);
|
const variable = this.getFormVariable(variableId);
|
||||||
if (variable) {
|
if (variable) {
|
||||||
variable.value = value;
|
variable.value = value;
|
||||||
|
variable.runtimeSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a form variable has been given a value at runtime, for example by a form rule.
|
||||||
|
*
|
||||||
|
* @param identifier The `name` or `id` value
|
||||||
|
* @returns `true` when the value was set at runtime rather than coming from the form definition
|
||||||
|
*/
|
||||||
|
isVariableSetAtRuntime(identifier: string): boolean {
|
||||||
|
return !!this.getFormVariable(identifier)?.runtimeSet;
|
||||||
|
}
|
||||||
|
|
||||||
private loadInjectedFieldValidators(injectedFieldValidators: FormFieldValidator[]): void {
|
private loadInjectedFieldValidators(injectedFieldValidators: FormFieldValidator[]): void {
|
||||||
this.fieldValidators = injectedFieldValidators ? [...FORM_FIELD_VALIDATORS, ...injectedFieldValidators] : [...FORM_FIELD_VALIDATORS];
|
this.fieldValidators = injectedFieldValidators ? [...FORM_FIELD_VALIDATORS, ...injectedFieldValidators] : [...FORM_FIELD_VALIDATORS];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,5 @@
|
|||||||
export class TaskProcessVariableModel {
|
export class TaskProcessVariableModel {
|
||||||
id?: string;
|
id?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
value: string;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1002,4 +1002,169 @@ describe('WidgetVisibilityService', () => {
|
|||||||
expect(textField.isVisible).toBe(true);
|
expect(textField.isVisible).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Visibility calculation from form variables', () => {
|
||||||
|
const hiddenWhileRequestor = new WidgetVisibilityModel({
|
||||||
|
leftType: 'variable',
|
||||||
|
leftValue: 'person_type',
|
||||||
|
operator: '!=',
|
||||||
|
rightType: 'value',
|
||||||
|
rightValue: 'Requestor',
|
||||||
|
nextConditionOperator: '',
|
||||||
|
nextCondition: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const requestorProcessVariables = [{ id: 'variables.person_type', value: 'Requestor' }];
|
||||||
|
|
||||||
|
let formJson: any;
|
||||||
|
let form: FormModel;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
formJson = {
|
||||||
|
id: 'person-type-form',
|
||||||
|
variables: [{ id: 'person-type-var', name: 'person_type', value: null }],
|
||||||
|
processVariables: [{ name: 'variables.person_type', value: 'Requestor' }]
|
||||||
|
};
|
||||||
|
form = new FormModel(formJson);
|
||||||
|
service.cleanProcessVariable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve a variable from the form when the cached process variables no longer hold it', () => {
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
|
||||||
|
service.refreshVisibility(new FormModel({ id: 'another-form' }), [{ id: 'variables.other', value: 'other' }]);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(form, hiddenWhileRequestor)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve a variable from the form when the refreshed data omits it', () => {
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
service.refreshVisibility(form, [{ id: 'processOutput', value: 'result' }]);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(form, hiddenWhileRequestor)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer a process variable over the value defined in the form definition', () => {
|
||||||
|
const formWithDefault = new FormModel({
|
||||||
|
id: 'person-type-form-with-default',
|
||||||
|
variables: [{ id: 'person-type-var', name: 'person_type', value: 'Approver' }],
|
||||||
|
processVariables: [{ name: 'variables.person_type', value: 'Requestor' }]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(service.getVariableValue(formWithDefault, 'person_type', requestorProcessVariables)).toBe('Requestor');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer a variable changed at runtime over a process variable of the same name', () => {
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
|
||||||
|
form.changeVariableValue('person-type-var', 'Approver');
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(form, hiddenWhileRequestor)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prefer a variable cleared at runtime over a process variable of the same name', () => {
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
|
||||||
|
form.changeVariableValue('person-type-var', '');
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(form, hiddenWhileRequestor)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep a variable changed at runtime when the form is rebuilt from the same definition', () => {
|
||||||
|
service.refreshVisibility(form, requestorProcessVariables);
|
||||||
|
form.changeVariableValue('person-type-var', 'Approver');
|
||||||
|
|
||||||
|
const rebuiltForm = new FormModel(formJson);
|
||||||
|
service.refreshVisibility(rebuiltForm, [{ id: 'processOutput', value: 'result' }]);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(rebuiltForm, hiddenWhileRequestor)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not convert the type of a variable changed at runtime', () => {
|
||||||
|
const numericForm = new FormModel({ id: 'numeric-form', variables: [{ id: 'amount-var', name: 'amount', type: 'integer', value: 0 }] });
|
||||||
|
const booleanForm = new FormModel({ id: 'boolean-form', variables: [{ id: 'flag-var', name: 'flag', type: 'boolean', value: true }] });
|
||||||
|
|
||||||
|
service.refreshVisibility(numericForm);
|
||||||
|
service.refreshVisibility(booleanForm);
|
||||||
|
|
||||||
|
numericForm.changeVariableValue('amount-var', 5);
|
||||||
|
booleanForm.changeVariableValue('flag-var', false);
|
||||||
|
|
||||||
|
service.refreshVisibility(numericForm);
|
||||||
|
service.refreshVisibility(booleanForm);
|
||||||
|
|
||||||
|
expect(service.getVariableValue(numericForm, 'amount')).toBe(5);
|
||||||
|
expect(service.getVariableValue(booleanForm, 'flag', [])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate a numeric variable changed at runtime without string comparison', () => {
|
||||||
|
const numericForm = new FormModel({
|
||||||
|
id: 'numeric-visibility-form',
|
||||||
|
variables: [{ id: 'amount-var', name: 'amount', type: 'integer', value: 0 }]
|
||||||
|
});
|
||||||
|
const amountOverTen = new WidgetVisibilityModel({
|
||||||
|
leftType: 'variable',
|
||||||
|
leftValue: 'amount',
|
||||||
|
operator: '>',
|
||||||
|
rightType: 'value',
|
||||||
|
rightValue: '10',
|
||||||
|
nextConditionOperator: '',
|
||||||
|
nextCondition: null
|
||||||
|
});
|
||||||
|
|
||||||
|
service.refreshVisibility(numericForm);
|
||||||
|
numericForm.changeVariableValue('amount-var', 5);
|
||||||
|
service.refreshVisibility(numericForm);
|
||||||
|
|
||||||
|
expect(service.evaluateVisibility(numericForm, amountOverTen)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve a zero process variable from the form when the refreshed data omits it', () => {
|
||||||
|
const zeroForm = new FormModel({
|
||||||
|
id: 'zero-form',
|
||||||
|
variables: [{ id: 'amount-var', name: 'amount', type: 'integer', value: 99 }],
|
||||||
|
processVariables: [{ name: 'variables.amount', value: 0, type: 'integer' }]
|
||||||
|
});
|
||||||
|
const amountIsZero = new WidgetVisibilityModel({
|
||||||
|
leftType: 'variable',
|
||||||
|
leftValue: 'amount',
|
||||||
|
operator: '==',
|
||||||
|
rightType: 'value',
|
||||||
|
rightValue: '0',
|
||||||
|
nextConditionOperator: '',
|
||||||
|
nextCondition: null
|
||||||
|
});
|
||||||
|
|
||||||
|
service.refreshVisibility(zeroForm, [{ id: 'variables.amount', value: 0 }]);
|
||||||
|
service.refreshVisibility(zeroForm, [{ id: 'processOutput', value: 'result' }]);
|
||||||
|
|
||||||
|
expect(service.getVariableValue(zeroForm, 'amount', [])).toBe(0);
|
||||||
|
expect(service.evaluateVisibility(zeroForm, amountIsZero)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve a false process variable from the form when the refreshed data omits it', () => {
|
||||||
|
const falseForm = new FormModel({
|
||||||
|
id: 'false-form',
|
||||||
|
variables: [{ id: 'flag-var', name: 'flag', type: 'boolean', value: true }],
|
||||||
|
processVariables: [{ name: 'variables.flag', value: false, type: 'boolean' }]
|
||||||
|
});
|
||||||
|
const flagIsFalse = new WidgetVisibilityModel({
|
||||||
|
leftType: 'variable',
|
||||||
|
leftValue: 'flag',
|
||||||
|
operator: '==',
|
||||||
|
rightType: 'value',
|
||||||
|
rightValue: 'false',
|
||||||
|
nextConditionOperator: '',
|
||||||
|
nextCondition: null
|
||||||
|
});
|
||||||
|
|
||||||
|
service.refreshVisibility(falseForm, [{ id: 'variables.flag', value: false }]);
|
||||||
|
service.refreshVisibility(falseForm, [{ id: 'processOutput', value: 'result' }]);
|
||||||
|
|
||||||
|
expect(service.getVariableValue(falseForm, 'flag', [])).toBe(false);
|
||||||
|
expect(service.evaluateVisibility(falseForm, flagIsFalse)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { FormService } from './form.service';
|
|||||||
export class WidgetVisibilityService {
|
export class WidgetVisibilityService {
|
||||||
private readonly formService = inject(FormService);
|
private readonly formService = inject(FormService);
|
||||||
|
|
||||||
private processVarList: TaskProcessVariableModel[];
|
private processVarList: TaskProcessVariableModel[] = [];
|
||||||
private form: FormModel;
|
private form: FormModel;
|
||||||
|
|
||||||
public refreshVisibility(form: FormModel, processVarList?: TaskProcessVariableModel[]) {
|
public refreshVisibility(form: FormModel, processVarList?: TaskProcessVariableModel[]) {
|
||||||
@@ -110,7 +110,7 @@ export class WidgetVisibilityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLeftValue(form: FormModel, visibilityObj: WidgetVisibilityModel): string {
|
getLeftValue(form: FormModel, visibilityObj: WidgetVisibilityModel): any {
|
||||||
let leftValue = '';
|
let leftValue = '';
|
||||||
if (visibilityObj.leftType === WidgetTypeEnum.variable) {
|
if (visibilityObj.leftType === WidgetTypeEnum.variable) {
|
||||||
leftValue = this.getVariableValue(form, visibilityObj.leftValue, this.processVarList);
|
leftValue = this.getVariableValue(form, visibilityObj.leftValue, this.processVarList);
|
||||||
@@ -124,7 +124,7 @@ export class WidgetVisibilityService {
|
|||||||
return leftValue;
|
return leftValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRightValue(form: FormModel, visibilityObj: WidgetVisibilityModel): string {
|
getRightValue(form: FormModel, visibilityObj: WidgetVisibilityModel): any {
|
||||||
let valueFound = '';
|
let valueFound = '';
|
||||||
|
|
||||||
if (visibilityObj.rightType === WidgetTypeEnum.variable) {
|
if (visibilityObj.rightType === WidgetTypeEnum.variable) {
|
||||||
@@ -270,21 +270,22 @@ export class WidgetVisibilityService {
|
|||||||
return field.id && fieldToFind ? field.id.toUpperCase() === fieldToFind.toUpperCase() : false;
|
return field.id && fieldToFind ? field.id.toUpperCase() === fieldToFind.toUpperCase() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getVariableValue(form: FormModel, name: string, processVarList: TaskProcessVariableModel[]): string {
|
public getVariableValue(form: FormModel, name: string, processVarList: TaskProcessVariableModel[] = []): any {
|
||||||
const processVariableValue = this.getProcessVariableValue(name, processVarList);
|
if (form.isVariableSetAtRuntime(name)) {
|
||||||
const variableDefaultValue = form.getDefaultFormVariableValue(name);
|
return form.getDefaultFormVariableValue(name);
|
||||||
|
|
||||||
return processVariableValue === undefined ? variableDefaultValue : processVariableValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getProcessVariableValue(name: string, processVarList: TaskProcessVariableModel[]): string {
|
const processVariableValue = this.getProcessVariableValue(name, processVarList);
|
||||||
if (processVarList) {
|
|
||||||
|
return processVariableValue === undefined ? form.getProcessVariableValue(name) : processVariableValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProcessVariableValue(name: string, processVarList: TaskProcessVariableModel[]): any {
|
||||||
const processVariable = processVarList.find((variable) => variable.id === name || variable.id === `variables.${name}`);
|
const processVariable = processVarList.find((variable) => variable.id === name || variable.id === `variables.${name}`);
|
||||||
|
|
||||||
if (processVariable) {
|
if (processVariable) {
|
||||||
return processVariable.value;
|
return processVariable.value;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2575,3 +2575,126 @@ describe('FormCloudComponent — runtime state preservation on data refresh', ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('FormCloudComponent — form variable visibility on data refresh', () => {
|
||||||
|
let fixture: ComponentFixture<FormCloudComponent>;
|
||||||
|
let formComponent: FormCloudComponent;
|
||||||
|
let visibilityService: WidgetVisibilityService;
|
||||||
|
|
||||||
|
/** field is hidden while person_type is Requestor */
|
||||||
|
const personTypeFormJson = {
|
||||||
|
id: 'person-type-form',
|
||||||
|
name: 'Person Type Form',
|
||||||
|
variables: [{ id: 'person-type-var', name: 'person_type', value: null }],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldType: 'ContainerRepresentation',
|
||||||
|
id: 'container1',
|
||||||
|
name: 'Container',
|
||||||
|
type: 'container',
|
||||||
|
tab: null,
|
||||||
|
numberOfColumns: 1,
|
||||||
|
fields: {
|
||||||
|
1: [
|
||||||
|
{
|
||||||
|
fieldType: 'FormFieldRepresentation',
|
||||||
|
id: 'conditionalField',
|
||||||
|
name: 'Conditional Field',
|
||||||
|
type: 'multiline-text',
|
||||||
|
value: null,
|
||||||
|
required: false,
|
||||||
|
readOnly: false,
|
||||||
|
visibilityCondition: {
|
||||||
|
leftType: 'variable',
|
||||||
|
leftValue: 'person_type',
|
||||||
|
operator: '!=',
|
||||||
|
rightType: 'value',
|
||||||
|
rightValue: 'Requestor',
|
||||||
|
nextConditionOperator: '',
|
||||||
|
nextCondition: null
|
||||||
|
},
|
||||||
|
params: { existingColspan: 1, maxColspan: 1 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestorVariables = () => [new TaskVariableCloud({ name: 'variables.person_type', value: 'Requestor' })];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [NoopTranslateModule, NoopAuthModule, FormCloudComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: VersionCompatibilityService, useValue: {} },
|
||||||
|
{ provide: FormRenderingService, useClass: CloudFormRenderingService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const apiService = TestBed.inject(AlfrescoApiService);
|
||||||
|
spyOn(apiService, 'getInstance').and.returnValue(mockOauth2Auth);
|
||||||
|
|
||||||
|
visibilityService = TestBed.inject(WidgetVisibilityService);
|
||||||
|
visibilityService.cleanProcessVariable();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(FormCloudComponent);
|
||||||
|
formComponent = fixture.componentInstance;
|
||||||
|
|
||||||
|
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(personTypeFormJson)));
|
||||||
|
formComponent.formCloudRepresentationJSON.processVariables = requestorVariables();
|
||||||
|
formComponent.data = requestorVariables();
|
||||||
|
formComponent.form = formComponent.parseForm(formComponent.formCloudRepresentationJSON);
|
||||||
|
visibilityService.refreshVisibility(formComponent.form, formComponent.data);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep the field hidden when the refreshed data omits the variable', () => {
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeFalse();
|
||||||
|
|
||||||
|
const partialData = [new TaskVariableCloud({ name: 'processOutput', value: 'result' })];
|
||||||
|
const change = new SimpleChange(formComponent.data, partialData, false);
|
||||||
|
formComponent.data = partialData;
|
||||||
|
|
||||||
|
formComponent.ngOnChanges({ data: change });
|
||||||
|
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the field when the refreshed data changes the variable', () => {
|
||||||
|
const approverData = [new TaskVariableCloud({ name: 'variables.person_type', value: 'Approver' })];
|
||||||
|
const change = new SimpleChange(formComponent.data, approverData, false);
|
||||||
|
formComponent.data = approverData;
|
||||||
|
|
||||||
|
formComponent.ngOnChanges({ data: change });
|
||||||
|
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep a variable changed by a form rule when the refreshed data omits it', () => {
|
||||||
|
formComponent.form.changeVariableValue('person-type-var', 'Approver');
|
||||||
|
|
||||||
|
const partialData = [new TaskVariableCloud({ name: 'processOutput', value: 'result' })];
|
||||||
|
const change = new SimpleChange(formComponent.data, partialData, false);
|
||||||
|
formComponent.data = partialData;
|
||||||
|
|
||||||
|
formComponent.ngOnChanges({ data: change });
|
||||||
|
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep the latest received variable value across a following partial refresh', () => {
|
||||||
|
const approverData = [new TaskVariableCloud({ name: 'variables.person_type', value: 'Approver' })];
|
||||||
|
formComponent.data = approverData;
|
||||||
|
formComponent.ngOnChanges({ data: new SimpleChange(requestorVariables(), approverData, false) });
|
||||||
|
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeTrue();
|
||||||
|
|
||||||
|
const partialData = [new TaskVariableCloud({ name: 'processOutput', value: 'result' })];
|
||||||
|
formComponent.data = partialData;
|
||||||
|
formComponent.ngOnChanges({ data: new SimpleChange(approverData, partialData, false) });
|
||||||
|
|
||||||
|
expect(formComponent.form.getFieldById('conditionalField').isVisible).toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -601,6 +601,8 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
private refreshFormData(previousData: TaskVariableCloud[] = []) {
|
private refreshFormData(previousData: TaskVariableCloud[] = []) {
|
||||||
const snapshot = this.snapshotRuntimeState();
|
const snapshot = this.snapshotRuntimeState();
|
||||||
|
|
||||||
|
this.mergeProcessVariables(this.data ?? []);
|
||||||
|
|
||||||
this.form = this.parseForm(this.formCloudRepresentationJSON);
|
this.form = this.parseForm(this.formCloudRepresentationJSON);
|
||||||
if (!this.form) {
|
if (!this.form) {
|
||||||
return;
|
return;
|
||||||
@@ -610,13 +612,36 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
|
|||||||
this.restoreRuntimeState(this.form, snapshot, changedFieldIds);
|
this.restoreRuntimeState(this.form, snapshot, changedFieldIds);
|
||||||
|
|
||||||
this.setCheckParentVisibilityForValidationOnFields();
|
this.setCheckParentVisibilityForValidationOnFields();
|
||||||
this.visibilityService.refreshVisibility(this.form);
|
this.visibilityService.refreshVisibility(this.form, this.data);
|
||||||
this.form.validateForm();
|
this.form.validateForm();
|
||||||
this.onFormLoaded(this.form);
|
this.onFormLoaded(this.form);
|
||||||
this.formService.formRulesEvent.next(new FormRulesEvent('dataRefreshed', new FormEvent(this.form)));
|
this.formService.formRulesEvent.next(new FormRulesEvent('dataRefreshed', new FormEvent(this.form)));
|
||||||
this.onFormDataRefreshed(this.form);
|
this.onFormDataRefreshed(this.form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the process variables on the stored representation up to date with the latest data, so that a
|
||||||
|
* variable omitted by a later partial refresh still resolves to the most recent value received.
|
||||||
|
*
|
||||||
|
* @param updates Variables received on the latest data refresh
|
||||||
|
*/
|
||||||
|
private mergeProcessVariables(updates: TaskVariableCloud[]): void {
|
||||||
|
if (!this.formCloudRepresentationJSON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing: TaskVariableCloud[] = this.formCloudRepresentationJSON.processVariables ?? [];
|
||||||
|
const byName = new Map<string, TaskVariableCloud>();
|
||||||
|
|
||||||
|
for (const variable of [...existing, ...updates]) {
|
||||||
|
if (variable?.name) {
|
||||||
|
byName.set(variable.name, variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formCloudRepresentationJSON.processVariables = Array.from(byName.values());
|
||||||
|
}
|
||||||
|
|
||||||
private snapshotRuntimeState(): Map<string, FormFieldRuntimeState> {
|
private snapshotRuntimeState(): Map<string, FormFieldRuntimeState> {
|
||||||
const snapshot = new Map<string, FormFieldRuntimeState>();
|
const snapshot = new Map<string, FormFieldRuntimeState>();
|
||||||
if (!this.form) {
|
if (!this.form) {
|
||||||
|
|||||||
Reference in New Issue
Block a user