diff --git a/src/mixins/extensions/LoopContainer.js b/src/mixins/extensions/LoopContainer.js
index 3914a7cca..8eca236a1 100644
--- a/src/mixins/extensions/LoopContainer.js
+++ b/src/mixins/extensions/LoopContainer.js
@@ -25,6 +25,7 @@ export default {
items: element.items,
},
],
+ watchers: definition.watchers,
};
let loopContext = '';
diff --git a/src/mixins/extensions/Watchers.js b/src/mixins/extensions/Watchers.js
index d374bcb70..a4a79da07 100644
--- a/src/mixins/extensions/Watchers.js
+++ b/src/mixins/extensions/Watchers.js
@@ -2,17 +2,24 @@ import watchersMixin from '../../mixins/watchers';
export default {
methods: {
+ filterWatchers(watcher) {
+ const inContext = !watcher.watching || this.variables.find(({name}) => name === watcher.watching);
+ return inContext;
+ },
addWatcherVariables(definition) {
if (definition.watchers) {
- definition.watchers.forEach((watcher) => {
- this.registerVariable(watcher.output_variable, {});
+ definition.watchers.filter(this.filterWatchers).forEach((watcher) => {
+ const inContext = !watcher.watching || this.variables.find(({name}) => name === watcher.watching);
+ if (inContext) {
+ this.registerVariable(watcher.output_variable, {});
+ }
});
}
},
watchers(screen, definition) {
if (definition.watchers) {
screen.mixins.push(watchersMixin);
- definition.watchers.forEach((watcher) => {
+ definition.watchers.filter(this.filterWatchers).forEach((watcher) => {
this.addMounted(screen, `
this.$nextTick(() => this.$watch('${watcher.watching}', (newValue) => {
if (typeof newValue !== 'undefined') {
diff --git a/tests/e2e/fixtures/watcher_inside_loop.json b/tests/e2e/fixtures/watcher_inside_loop.json
new file mode 100644
index 000000000..437ed7379
--- /dev/null
+++ b/tests/e2e/fixtures/watcher_inside_loop.json
@@ -0,0 +1 @@
+{"type":"screen_package","version":"2","screens":[{"id":5,"screen_category_id":"1","title":"Watcher in Loop","description":"Watcher in Loop","type":"FORM","config":[{"name":"Watcher in Loop","items":[{"items":[{"label":"Line Input","config":{"icon":"far fa-square","name":"form_input_1","type":"text","label":"New Input","helper":null,"readonly":false,"dataFormat":"string","validation":[],"placeholder":null},"component":"FormInput","inspector":[{"type":"FormInput","field":"name","config":{"name":"Variable Name","label":"Variable Name","helper":"A variable name is a symbolic name to reference information.","validation":"regex:\/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*[^.]$\/|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false"}},{"type":"FormInput","field":"label","config":{"label":"Label","helper":"The label describes the field's name"}},{"type":"FormMultiselect","field":"dataFormat","config":{"name":"Data Type","label":"Data Type","helper":"The data type specifies what kind of data is stored in the variable.","options":[{"value":"string","content":"Text"},{"value":"int","content":"Integer"},{"value":"currency","content":"Currency"},{"value":"percentage","content":"Percentage"},{"value":"float","content":"Decimal"},{"value":"datetime","content":"Datetime"},{"value":"date","content":"Date"},{"value":"password","content":"Password"}],"validation":"required"}},{"type":{"extends":{"props":["label","error","options","helper","name","value","selectedControl"],"mixins":[{"props":{"validation":{"type":null},"validationData":{"type":null},"validationField":{"type":null},"validationMessages":{"type":null}},"watch":{"validationData":{"deep":true}},"methods":[],"computed":[]}],"methods":[],"computed":[],"_compiled":true,"inheritAttrs":false,"staticRenderFns":[]},"computed":[],"_compiled":true,"staticRenderFns":[]},"field":"dataMask","config":{"name":"Data Format","label":"Data Format","helper":"The data format for the selected type."}},{"type":"ValidationSelect","field":"validation","config":{"label":"Validation Rules","helper":"The validation rules needed for this field"}},{"type":"FormInput","field":"placeholder","config":{"label":"Placeholder Text","helper":"The placeholder is what is shown in the field when no value is provided yet"}},{"type":"FormInput","field":"helper","config":{"label":"Helper Text","helper":"Help text is meant to provide additional guidance on the field's value"}},{"type":"FormCheckbox","field":"readonly","config":{"label":"Read Only","helper":null}},{"type":"ColorSelect","field":"color","config":{"label":"Text Color","helper":"Set the element's text color","options":[{"value":"text-primary","content":"primary"},{"value":"text-secondary","content":"secondary"},{"value":"text-success","content":"success"},{"value":"text-danger","content":"danger"},{"value":"text-warning","content":"warning"},{"value":"text-info","content":"info"},{"value":"text-light","content":"light"},{"value":"text-dark","content":"dark"}]}},{"type":"ColorSelect","field":"bgcolor","config":{"label":"Background Color","helper":"Set the element's background color","options":[{"value":"alert alert-primary","content":"primary"},{"value":"alert alert-secondary","content":"secondary"},{"value":"alert alert-success","content":"success"},{"value":"alert alert-danger","content":"danger"},{"value":"alert alert-warning","content":"warning"},{"value":"alert alert-info","content":"info"},{"value":"alert alert-light","content":"light"},{"value":"alert alert-dark","content":"dark"}]}},{"type":{"props":["value","helper"],"watch":{"value":{"immediate":true}},"methods":[],"_scopeId":"data-v-172d71e1","computed":{"effectiveValue":[]},"_compiled":true,"components":{"MonacoEditor":{"name":"monaco-editor","_Ctor":[],"props":{"amdRequire":[]},"extends":{"name":"MonacoEditor","model":{"event":"change"},"props":{"theme":{"default":"vs"},"value":{"required":true},"options":[],"language":[],"original":[],"amdRequire":[],"diffEditor":{"default":false}},"watch":{"options":{"deep":true,"user":true}},"methods":[]},"methods":[]}},"staticRenderFns":[]},"field":"defaultValue","config":{"label":"Default Value","helper":"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed."}},{"type":"FormInput","field":"conditionalHide","config":{"label":"Visibility Rule","helper":"This control is hidden until this expression is true"}},{"type":"FormInput","field":"customFormatter","config":{"label":"Custom Format String","helper":"Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####","validation":null}},{"type":"FormInput","field":"customCssSelector","config":{"label":"CSS Selector Name","helper":"Use this in your custom css rules","validation":"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]"}},{"type":"FormInput","field":"ariaLabel","config":{"label":"Aria Label","helper":"Attribute designed to help assistive technology (e.g. screen readers) attach a label"}},{"type":"FormInput","field":"tabindex","config":{"label":"Tab Order","helper":"Order in which a user will move focus from one control to another by pressing the Tab key","validation":"regex: [0-9]*"}}],"editor-control":"FormInput","editor-component":"FormInput"}],"label":"Loop","config":{"icon":"fas fa-redo","name":"loop_1","label":null,"settings":{"add":false,"type":"new","times":"3","varname":"loop_1"}},"component":"FormLoop","container":true,"inspector":[{"type":"LoopInspector","field":"settings","config":[]},{"type":"FormInput","field":"conditionalHide","config":{"label":"Visibility Rule","helper":"This control is hidden until this expression is true"}},{"type":"FormInput","field":"customFormatter","config":{"label":"Custom Format String","helper":"Use the Mask Pattern format
Date ##\/##\/####
SSN ###-##-####
Phone (###) ###-####","validation":null}},{"type":"FormInput","field":"customCssSelector","config":{"label":"CSS Selector Name","helper":"Use this in your custom css rules","validation":"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]"}},{"type":"FormInput","field":"ariaLabel","config":{"label":"Aria Label","helper":"Attribute designed to help assistive technology (e.g. screen readers) attach a label"}},{"type":"FormInput","field":"tabindex","config":{"label":"Tab Order","helper":"Order in which a user will move focus from one control to another by pressing the Tab key","validation":"regex: [0-9]*"}}],"editor-control":"Loop","editor-component":"Loop"}]}],"computed":[],"custom_css":null,"created_at":"2021-11-05T15:35:12-07:00","updated_at":"2021-11-05T15:37:47-07:00","status":"ACTIVE","key":null,"watchers":[{"input_data":"{}","script_configuration":"{}","synchronous":false,"show_async_loading":false,"run_onload":false,"name":"watcher inside loop","watching":"form_input_1","output_variable":"inside_loop","script":{"id":"script-6","key":null,"title":"simple","description":"simple","language":"php","code":"users()->getUserById(1)['email'] \n * API Documentation https:\/\/github.com\/ProcessMaker\/docker-executor-php\/tree\/master\/docs\/sdk \n *\/\n\nreturn [\"foo\"=>\"bar\"];","timeout":60,"run_as_user_id":2,"created_at":"2021-11-05T15:36:35-07:00","updated_at":"2021-11-05T15:36:52-07:00","status":"ACTIVE","script_category_id":"1","script_executor_id":3},"script_id":"6","script_key":null,"uid":"16361517695581"}],"categories":[{"id":1,"name":"Uncategorized","status":"ACTIVE","is_system":0,"created_at":"2021-11-01T11:16:52-07:00","updated_at":"2021-11-01T11:16:52-07:00","pivot":{"assignable_id":5,"category_id":1,"category_type":"ProcessMaker\\Models\\ScreenCategory"}}]}],"screen_categories":[],"scripts":[{"id":6,"key":null,"title":"simple","description":"simple","language":"php","code":"users()->getUserById(1)['email'] \n * API Documentation https:\/\/github.com\/ProcessMaker\/docker-executor-php\/tree\/master\/docs\/sdk \n *\/\n\nreturn [\"foo\"=>\"bar\"];","timeout":60,"run_as_user_id":2,"created_at":"2021-11-05T15:36:35-07:00","updated_at":"2021-11-05T15:36:52-07:00","status":"ACTIVE","script_category_id":"1","script_executor_id":3,"categories":[{"id":1,"name":"Uncategorized","status":"ACTIVE","is_system":0,"created_at":"2021-11-01T11:16:52-07:00","updated_at":"2021-11-01T11:16:52-07:00","pivot":{"assignable_id":6,"category_id":1,"category_type":"ProcessMaker\\Models\\ScriptCategory"}}]}]}
\ No newline at end of file
diff --git a/tests/e2e/specs/LoadComplexData.spec.js b/tests/e2e/specs/LoadComplexData.spec.js
index e57be9199..b65b1cd9e 100644
--- a/tests/e2e/specs/LoadComplexData.spec.js
+++ b/tests/e2e/specs/LoadComplexData.spec.js
@@ -8,7 +8,7 @@ describe('Validation Rules (Advanced test)', () => {
cy.loadFromJson('FPP_PFP_CHAIRS_SCREEN.json', 0);
cy.get('[data-cy=mode-preview]').click();
// Open Tab 2
- cy.get('[aria-label="Part Time"]:visible:first').click();
+ cy.get('button:contains("Part Time"):visible:first').click();
// Tab 2 contains the title '2. PART TIME PAGE'
cy.get('#preview').should('contain.html', '2. PART TIME PAGE');
diff --git a/tests/e2e/specs/WatcherInsideLoop.spec.js b/tests/e2e/specs/WatcherInsideLoop.spec.js
new file mode 100644
index 000000000..b9a7f8676
--- /dev/null
+++ b/tests/e2e/specs/WatcherInsideLoop.spec.js
@@ -0,0 +1,114 @@
+describe('watcher inside loop', () => {
+ beforeEach(() => {
+ cy.server();
+ cy.visit('/');
+ cy.route(
+ 'POST',
+ '/api/1.0/scripts/execute/6',
+ JSON.stringify({
+ output: {
+ foo: 'bar',
+ },
+ })
+ ).as('executeScript');
+ });
+
+ it('Verify watcher is placed inside loop', () => {
+ cy.loadFromJson('watcher_inside_loop.json', 0);
+ cy.get('[data-cy=mode-preview]').click();
+
+ // Check the data of the screen
+ cy.assertPreviewData({
+ 'loop_1': [
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ ],
+ });
+
+ // Change form_input_1 to trigger watcher in the first loop
+ cy.get('[data-cy="screen-field-form_input_1"]').eq(0).type('foo');
+ cy.wait('@executeScript');
+ // Check the data of the screen
+ cy.assertPreviewData({
+ 'loop_1': [
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ ],
+ });
+
+ // Change form_input_1 to trigger watcher in the second loop
+ cy.get('[data-cy="screen-field-form_input_1"]').eq(1).type('foo');
+ cy.wait('@executeScript');
+ // Check the data of the screen
+ cy.assertPreviewData({
+ 'loop_1': [
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ {
+ 'form_input_1': '',
+ 'inside_loop': null,
+ },
+ ],
+ });
+
+ // Change form_input_1 to trigger watcher in the third loop
+ cy.get('[data-cy="screen-field-form_input_1"]').eq(2).type('foo');
+ cy.wait('@executeScript');
+ // Check the data of the screen
+ cy.assertPreviewData({
+ 'loop_1': [
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ {
+ 'form_input_1': 'foo',
+ 'inside_loop': {
+ foo: 'bar',
+ },
+ },
+ ],
+ });
+
+ });
+});