Issue with Disabling and Enabling the Submit Button in Grafana Data Manipulation Plugin Form

Grafana Version and Operating System: I am using Grafana version 10.0.2 on Rasbian Operating System.

Objective: I am attempting to implement a feature in my Grafana Data Manipulation plugin form where the Submit button should be temporarily disabled upon receiving a successful response from a Node-Red server. This will prevent users from making repeated submissions and provide feedback on the success of their request.

Approach: I have implemented the following approach to achieve this functionality:

  1. When the user clicks the Submit button in my Grafana Data Manipulation plugin form, a request is sent to a Node-Red server.
  2. Upon receiving a response from the server, I aim to perform the following actions:
  • If the response is successful (response.ok), I intend to:
    • Disable the Submit button to prevent multiple submissions.
    • Display a success notification to inform the user that their values were updated successfully.
    • Wait for a few seconds to allow the pH controller to stabilize.
    • After the delay, re-enable the Submit button to allow the user to make another submission.
  • If the response indicates an error, I should:
    • Display an error notification to inform the user of the issue.

Current Status: The code I’ve written to achieve this functionality looks like this:

Custom Code in Update Request.

if (response && response.ok) {
  // Display a success message for values update
  notifySuccess(['Update', 'Values updated successfully.']);

  // Display a message indicating that the controller is controlling pH
  notifySuccess(['pH Controller', 'Wait for a few seconds until pH is set to the desired pH']);

  // Disable the submit button
  var submitButton = document.querySelector('button[aria-label="Send"]');
  if (submitButton) {
    submitButton.disabled = true;
  }

  // Simulate a delay before enabling the submit button (adjust the delay time as needed)
  setTimeout(function () {
    if (submitButton) {
      submitButton.disabled = false; // Enable the submit button
    }

    // Display a message indicating that the controller is ready for the next update
    notifySuccess(['pH Controller', 'Controller is ready for the next update.']);

    // Reload the page (you can uncomment this if needed)
    locationService.reload();
  }, 20000); // Adjust the delay time as needed (in milliseconds)
} else {
  // Display an error message
  notifyError(['Update', 'An error occurred updating values.']);
}

Expectation: I expect that upon receiving a successful response, the Submit button will be disabled, a success notification will be displayed, and after a brief delay, the button will be re-enabled. In the case of an error response, an error notification will be shown.

Configuration: I am using the Grafana Data Manipulation plugin, and the relevant configuration for the form and Submit button is as follows:

{
  "datasource": {
    "type": "influxdb",
    "uid": "ccbb3313-a521-40d4-9e26-42a8bea8f3fb"
  },
  "gridPos": {
    "h": 11,
    "w": 7,
    "x": 0,
    "y": 24
  },
  "id": 7,
  "options": {
    "layout": {
      "variant": "single",
      "padding": 20,
      "sections": []
    },
    "initial": {
      "method": "-",
      "contentType": "application/json",
      "code": "console.log(data, response, initial, elements)",
      "highlight": false,
      "highlightColor": "red"
    },
    "update": {
      "method": "POST",
      "contentType": "application/json",
      "updatedOnly": false,
      "code": "//if (response && response.ok) {\n//  notifySuccess(['Update', 'Values updated successfully.']);\n//locationService.reload();\n//  notifySuccess(['pH Controller', 'Wait for few seconds until pH is set to desired pH']);\n//} else {\n//  notifyError(['Update', 'An error occured updating values.']);\n//}\n\nif (response && response.ok) {\n  // Display a success message for values update\n  notifySuccess(['Update', 'Values updated successfully.']);\n\n  // Display a message indicating that the controller is controlling pH\n  notifySuccess(['pH Controller', 'Wait for a few seconds until pH is set to the desired pH']);\n\n  // Disable the submit button\n  var submitButton = document.querySelector('button[aria-label=\"Send\"]');\n  if (submitButton) {\n    submitButton.disabled = true;\n  }\n\n  // Simulate a delay before enabling the submit button (adjust the delay time as needed)\n  setTimeout(function () {\n    if (submitButton) {\n      submitButton.disabled = false; // Enable the submit button\n    }\n\n    // Display a message indicating that the controller is ready for the next update\n    notifySuccess(['pH Controller', 'Controller is ready for the next update.']);\n\n    // Reload the page (you can uncomment this if needed)\n    locationService.reload();\n  }, 20000); // Adjust the delay time as needed (in milliseconds)\n} else {\n  // Display an error message\n  notifyError(['Update', 'An error occurred updating values.']);\n}\n\n",
      "confirm": false,
      "url": "http://192.168.178.22:1880/setpoint"
    },
    "buttonGroup": {
      "orientation": "left",
      "size": "md"
    },
    "submit": {
      "variant": "custom",
      "foregroundColor": "yellow",
      "backgroundColor": "dark-green",
      "icon": "line",
      "text": "Send"
    },
    "reset": {
      "variant": "primary",
      "foregroundColor": "yellow",
      "backgroundColor": "purple",
      "icon": "process",
      "text": "Reset"
    },
    "elements": [
      {
        "id": "ph_value",
        "labelWidth": 12,
        "max": 15,
        "min": 0,
        "section": "",
        "step": 0.01,
        "title": "Desired pH",
        "tooltip": "",
        "type": "slider",
        "value": 4.23
      },
      {
        "id": "volume",
        "labelWidth": 20,
        "section": "",
        "title": "Solution Volume(in ml) ",
        "tooltip": "",
        "type": "number",
        "unit": "ml",
        "value": "1493",
        "width": 20
      },
      {
        "id": "motor_id",
        "labelWidth": 20,
        "options": [
          {
            "label": "Motor 1",
            "type": "number",
            "value": 1
          },
          {
            "label": "Motor 2",
            "type": "number",
            "value": 2
          },
          {
            "label": "Motor 3",
            "type": "number",
            "value": 3
          },
          {
            "label": "Motor 4",
            "type": "number",
            "value": 4
          }
        ],
        "section": "",
        "title": "Select Motor",
        "tooltip": "",
        "type": "select",
        "value": 2,
        "width": 20
      },
      {
        "id": "set_on_time",
        "labelWidth": 20,
        "section": "",
        "title": "Set On Time",
        "tooltip": "",
        "type": "number",
        "unit": "seconds",
        "value": "7",
        "width": 20
      },
      {
        "id": "set_off_time",
        "labelWidth": 20,
        "section": "",
        "title": "Set Off Time",
        "tooltip": "",
        "type": "number",
        "unit": "seconds",
        "value": "3",
        "width": 20
      }
    ]
  },
  "title": "pH Dosing Control",
  "type": "volkovlabs-form-panel"
}

Errors: I have not encountered any error, but the button is not being disabled.

I appreciate any assistance and guidance from the Grafana community to help me resolve this issue. Thank you!

@poojan05 It’s part of the latest release 3.2.1:

3.2.1 (2023-09-07)

Features / Enhancements

  • Add backward compatibility for option id (#244)

3.2.0 (2023-09-06)

Features / Enhancements

  • Add min and max date for date time element (#225)
  • Map Data Source values to elements (#224)
  • Update element Show If to support variables (#230)
  • Add clearing errors before initial and update requests (#232)
  • Add URL encode to variables (#231)
  • Allow empty section name (#228)
  • Add Query Field Picker for Initial Request (#227)
  • Add File element type for File Upload (#229)
  • Add converting option value to string and number based on type (#233)
  • Add loading states for Initial, Update and Reset button actions (#234)
  • Add support for asynchronous custom code (#234)
  • Update Query and Data Source initial request (#237)
  • Add icons for radio and select options (#238)

We also created a new tutorial to highlight the latest updates:

Hi @mikhailvolkov

Thanks for the reply!

I’m facing an issue with my Grafana dashboard configuration. I have a dashboard set to auto-refresh every 5 seconds, and it contains a panel that uses the Data Manipulation Plugin to send data to Node-RED. The problem is that when the dashboard refreshes, this specific panel resets to its default values, and the submit button becomes disabled. I want to prevent this panel from refreshing so that it retains the last user-entered value and keeps the submit button enabled.
Below is configuration of Panel.

{
  "datasource": {
    "type": "influxdb",
    "uid": "ccbb3313-a521-40d4-9e26-42a8bea8f3fb"
  },
  "gridPos": {
    "h": 11,
    "w": 7,
    "x": 0,
    "y": 24
  },
  "id": 7,
  "options": {
    "layout": {
      "variant": "single",
      "padding": 20,
      "orientation": "horizontal",
      "sections": [
        {
          "id": "",
          "name": ""
        }
      ]
    },
    "initial": {
      "method": "-",
      "contentType": "application/json",
      "code": "console.log(data, response, initial, elements)",
      "getPayload": "return {\n  rawSql: '',\n  format: 'table',\n}",
      "highlight": false,
      "highlightColor": "red",
      "datasource": "InfluxDB",
      "url": "http://192.168.178.22:1880/setpoint"
    },
    "update": {
      "method": "POST",
      "contentType": "application/json",
      "code": "\n//if (response && response.ok) {\n//  notifySuccess(['Update', 'Values updated successfully.']);\n//  locationService.reload();\n//  notifySuccess(['pH Controller', 'Wait for few seconds until pH is set to desired pH']);\n//} else {\n//  notifyError(['Update', 'An error occured updating values.']);\n//}\n\n//if (response && response.ok) {\n//  notifySuccess(['Update', 'Values updated successfully.']);\n//  locationService.reload();\n//  notifySuccess(['pH Controller', 'Wait for few seconds until pH is set to the desired pH']);\n\n  // Enable the submit button\n//  const submitButton = document.getElementById(SubmitEvent); // Replace 'submit' with your button's ID\n//  if (submitButton) {\n//    submitButton.disabled = false;\n//  }\n//} else {\n//  notifyError(['Update', 'An error occurred updating values.']);\n//}\n\n\n\n\n\n\n\n\n//if (response && response.ok) {\n  // Display a success message for values update\n//  notifySuccess(['Update', 'Values updated successfully.']);\n\n  // Display a message indicating that the controller is controlling pH\n//  notifySuccess(['pH Controller', 'Wait for a few seconds until pH is set to the desired pH']);\n\n  // Disable the submit button\n//  var submitButton = document.getElementById(submitButton);\n//  if (submitButton) {\n//    submitButton.disabled = true;\n//  }\n\n  // Simulate a delay before enabling the submit button (adjust the delay time as needed)\n//  setTimeout(function () {\n//    if (submitButton) {\n//      submitButton.disabled = false; // Enable the submit button\n//    }\n\n    // Display a message indicating that the controller is ready for the next update\n//    notifySuccess(['pH Controller', 'Controller is ready for the next update.']);\n\n    // Reload the page (you can uncomment this if needed)\n    //locationService.reload();\n//  }, 20000); // Adjust the delay time as needed (in milliseconds)\n//} else {\n//  // Display an error message\n//  notifyError(['Update', 'An error occurred updating values.']);\n//}\n\n",
      "confirm": true,
      "payloadMode": "all",
      "getPayload": "const payload = {};\n\nelements.forEach((element) => {\n  if (!element.value) {\n    return;\n  }\n\n  payload[element.id] = element.value;\n})\n\nreturn payload;\n\n/**\n * Data Source payload\n */\nreturn {\n  rawSql: '',\n  format: 'table',\n};",
      "header": [],
      "updatedOnly": false,
      "url": "http://192.168.178.22:1880/setpoint"
    },
    "buttonGroup": {
      "orientation": "left",
      "size": "md"
    },
    "submit": {
      "variant": "custom",
      "foregroundColor": "yellow",
      "backgroundColor": "dark-green",
      "icon": "line",
      "text": "Send"
    },
    "reset": {
      "variant": "primary",
      "foregroundColor": "yellow",
      "backgroundColor": "purple",
      "icon": "process",
      "text": "Reset"
    },
    "resetAction": {
      "mode": "custom",
      "code": "console.log(data, response, initial, elements);"
    },
    "saveDefault": {
      "variant": "hidden",
      "icon": "save",
      "text": "Save Default"
    },
    "elements": [
      {
        "id": "ph_value",
        "labelWidth": 12,
        "max": 15,
        "min": 0,
        "section": "",
        "step": 0.01,
        "title": "Desired pH",
        "tooltip": "",
        "type": "slider",
        "uid": "945ec294-d592-418c-ae1c-a999314a1bbd",
        "value": 4.23,
        "showIf": ""
      },
      {
        "id": "volume",
        "labelWidth": 20,
        "section": "",
        "title": "Solution Volume(in ml) ",
        "tooltip": "",
        "type": "number",
        "uid": "f3354772-831b-4ae6-99bf-a577e8adefab",
        "unit": "ml",
        "value": "400000",
        "width": 20
      },
      {
        "id": "motor_id",
        "labelWidth": 20,
        "options": [
          {
            "id": 1,
            "label": "Motor 1",
            "type": "number",
            "value": 1
          },
          {
            "id": 2,
            "label": "Motor 2",
            "type": "number",
            "value": 2
          },
          {
            "id": 3,
            "label": "Motor 3",
            "type": "number",
            "value": 3
          },
          {
            "id": 4,
            "label": "Motor 4",
            "type": "number",
            "value": 4
          }
        ],
        "section": "",
        "title": "Select Motor",
        "tooltip": "",
        "type": "select",
        "uid": "f1c3c720-71b1-4506-b7cf-8e62508384b1",
        "value": 1,
        "width": 20
      },
      {
        "id": "set_on_time",
        "labelWidth": 20,
        "section": "",
        "title": "Set On Time",
        "tooltip": "",
        "type": "number",
        "uid": "b932ed6a-6663-4af6-b3e0-03ced5b1bed5",
        "unit": "seconds",
        "value": "7",
        "width": 20
      },
      {
        "id": "set_off_time",
        "labelWidth": 20,
        "section": "",
        "title": "Set Off Time",
        "tooltip": "",
        "type": "number",
        "uid": "810503cb-bd83-4fb6-8506-ace13e03d89f",
        "unit": "seconds",
        "value": "3",
        "width": 20
      }
    ]
  },
  "targets": [
    {
      "datasource": {
        "type": "influxdb",
        "uid": "ccbb3313-a521-40d4-9e26-42a8bea8f3fb"
      },
      "groupBy": [
        {
          "params": [
            "$__interval"
          ],
          "type": "time"
        },
        {
          "params": [
            "null"
          ],
          "type": "fill"
        }
      ],
      "measurement": "NODE1",
      "orderByTime": "ASC",
      "policy": "autogen",
      "query": "SELECT mean(\"pH_Value\") FROM \"autogen\".\"NODE1\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
      "rawQuery": true,
      "refId": "A",
      "resultFormat": "time_series",
      "select": [
        [
          {
            "params": [
              "pH_Value"
            ],
            "type": "field"
          },
          {
            "params": [],
            "type": "mean"
          }
        ]
      ],
      "tags": []
    }
  ],
  "title": "pH Dosing Control",
  "type": "volkovlabs-form-panel",
  "transparent": true
}

Can you please help me with this, as I am new to this, I am facing difficulties.

Thanks in Advance!

@poojan05 We recently added an example of how to do it:

Hi @mikhailvolkov ,

I still facing issues of refreshing. I’m encountering a persistent issue with a custom Data Manipulation Panel in Grafana where it seems to be continuously refreshing, and the submit and reset buttons are disabled. I need help diagnosing and resolving this problem.

In the “Initial request” section of the panel configuration, I have placed custom code to handle user input and updates. The code should run once when the dashboard loads.

const elements = [
  {
    id: 'ph_value',
    value: '', // Leave it empty initially
  },
  {
    id: 'volume',
    value: '', // Leave it empty initially
  },
  {
    id: 'motor_id',
    value: '', // Leave it empty initially
  },
  {
    id: 'set_on_time',
    value: '', // Leave it empty initially
  },
  {
    id: 'set_off_time',
    value: '', // Leave it empty initially
  },
];

const getValues = async () => {
  /**
   * Check if all values are empty
   */
  const isFirstLoad = elements.every((element) => !element.value);

  if (isFirstLoad) {
    try {
      /**
       * Get Data using an API endpoint
       * Replace 'YOUR_API_ENDPOINT' with the actual API endpoint
       */
      const response = await fetch('YOUR_API_ENDPOINT');
      const json = await response.json();

      /**
       * Update initial element values
       */
      onChange(
        elements.map((element) => ({
          ...element,
          value: json[element.id],
        }))
      );
    } catch (error) {
      console.error('Error fetching data:', error);
    }
  }
};

// Call the getValues function to initialize the dashboard
getValues();

In update request I have use POST request and Below, is the custom code in Update request:

if (response && response.ok) {
  notifySuccess(['Update', 'Values updated successfully.']);
  //locationService.reload();
  notifySuccess(['pH Controller', 'Wait for few seconds until pH is set to desired pH']);
} else {
  notifyError(['Update', 'An error occured updating values.']);
}

Despite the code being set to run only on the initial load, the panel continuously refreshes, causing the submit and reset buttons to be disabled.

Can you please help me out?

Thanks in Advance!