Form doesn't execute a second time inside a BSTable

Wappler 7 Beta 14
Windows 11, NodeJS

A Server Connect Form inside a Bootstrap 5 Table Generator like this:

<tbody>
<tr>
<td class="text-center">
  <form id="form_registerVal" method="post" is="dmx-serverconnect-form" action="/api/test/update/confirm" dmx-on:success="run({run:{outputType:'text',action:`form_registerVal.reset(true)`}})" dmx-bind:id="'form_registerVal'+$index">
    <input id="inp_id_val" name="id_val" class="form-control" dmx-bind:value="id_val" type="hidden">
    <div class="row">
      <div class="col d-flex justify-content-center">
        <input id="inp_date" name="dateVal" is="dmx-date-picker" placeholder="Choose date" showdropdowns="true" minyear="1990">
        <button id="btn1" class="btn text-bg-primary ms-2" dmx-bind:disabled="inp_date.disabled||inp_date.invalid||inp_date.value.isEmpty()" dmx-on:click="run({'bootbox.confirm':{message:'Confirm Value',buttons:{confirm:{label:'Yes'},cancel:{label:'Cancel',className:'btn-danger'}},swapButtonOrder:true,then:{steps:{run:{outputType:'text',action:`form_registerVal.submit()`}}}}})">Confirm</button>
      </div>
    </div>
  </form>
</td>
.....

This column will display for each row a date field and a confirmation button. When choosing any row to submit the form, it runs fine, you can even see it in the XHR tab of the browser, however, when trying to run on a new row, the form does not run, the XHR tab does not even show any new form submitted.

Thanks

EDIT-----

I forgot to mention that the problem comes from the bootbox confirmation, when trying to submit a new form nothing happens, the bootbox confirmation button does not generate any action.

Update

I get this error on browser console every time I click on Confirm button inside the Bootbox.

Blocked aria-hidden on an element because its descendant retained focus. The focus must not be hidden from assistive technology users. Avoid using aria-hidden on a focused element or its ancestor. Consider using the inert attribute instead, which will also prevent focus. For more details, see the aria-hidden section of the WAI-ARIA specification at https://w3c.github.io/aria/#aria-hidden.
Element with focus: button
Ancestor with aria-hidden:  <div class=​"modal fade" id=​"modbox-1734150060665974" tabindex=​"-1" aria-labelledby=​"modbox-1734150060665974-title" style=​"display:​ none;​" aria-hidden=​"true">​…​</div>​

Edit:
In Wappler 6.8 works normally

New Update

If I add the dmx-on:success to the form to reload the bootstrap table data to update the rows that were deleted by the form this way, the bootbox generates the problem of not running a second time on a new row to submit the form. If I remove sc_tableList.load and the bootstrap table stays with the same data without updating the bootbox works normally. It seems that the problem comes when the tableRepeater updates the data and causes the bootbox to break.
In this case dmx-on:success runs with inline flows, but its the same result using normal Actions.

<form id="form_registerVal" method="post" is="dmx-serverconnect-form" action="/api/test/update/confirm" dmx-on:success="run({run:{outputType:'text',action:sc_tableList.load({});form_registerVal.reset(true)}})" dmx-bind:id="'form_registerVal'+$index">

Is the bootbox issue related to the form not posted or is it a separate issue? The error you get in the console is just informative, when the modal hides the button which is in the modal has still focus, this can cause unexpected problems as the button can still be pressed by pressing enter on the keyboard.

How is the repeat done, do you use a keyed repeater?

I'm using bootbox which aims to submit a form inside a Bootstrap 5 table generator, each row has a form and a button which allows to open the bootbox to confirm the submission.
This same form has a dmx-on:success="form_listTable.load({}) which reloads the data which the Bootstrap 5 table generator uses to populate the data, so when booxbot confirms the submission the first time it works normally and the table data is updated because the form originally deleted the current row, but the second time the bootbox doesn't trigger the submit action, it stops working, there is no action at all.

If I remove dmx-on:success="form_listTable.load({}) from the dynamic success event of the form, the bootbox works normally. The problem arises when the data in the table is updated.

Hi @patrick, maybe you were able to replicate the problem? Let me know if you need anything else.
Thanks

Are you using a unique key for your repeat region?

No, and as it is it work well in v6.8.0

Well, try adding a key to it and see if it makes any difference. The key should be any unique value from your data source such as the record id.

I have tested with unique key and it is the same.
We should modify all old projects to have a unique key, is that what you say?
Bootstrap 5 table generator default options. I always use it without unique key. As I said, version 6.8.0 works perfectly.

@patrick did you find the error?
Thanks

No, it's not what i say.

Hi @patrick, sorry to bother you but any news on this?

Could you send me the full code of your page in a private message or give me access to a test environment. I'm not able to replicate the problem with the current information and on a complex page many factors can influence each other's.

Sorry I can't send you my full code, but better yet, I've recreated the error on a new page:

SQL using MariaDB:

CREATE TABLE IF NOT EXISTS `locations` (
  `location_id` int(11) NOT NULL AUTO_INCREMENT,
  `street_address` varchar(40) DEFAULT NULL,
  `postal_code` varchar(12) DEFAULT NULL,
  `city` varchar(30) NOT NULL,
  `state_province` varchar(25) DEFAULT NULL,
  `country_id` char(2) NOT NULL,
  `state` tinyint(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`location_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2701 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

REPLACE INTO `locations` (`location_id`, `street_address`, `postal_code`, `city`, `state_province`, `country_id`, `state`) VALUES
	(1400, '2014 Jabberwocky Rd', '26192', 'Southlake', 'Texas', 'US', 0),
	(1500, '2011 Interiors Blvd', '99236', 'South San Francisco', 'California', 'US', 0),
	(1700, '2004 Charade Rd', '98199', 'Seattle', 'Washington', 'US', 0),
	(1800, '147 Spadina Ave', 'M5V 2L7', 'Toronto', 'Ontario', 'CA', 0),
	(2400, '8204 Arthur St', NULL, 'London', NULL, 'UK', 0),
	(2500, 'Magdalen Centre, The Oxford Science Park', 'OX9 9ZB', 'Oxford', 'Oxford', 'UK', 0),
	(2700, 'Schwanthalerstr. 7031', '80925', 'Munich', 'Bavaria', 'DE', 0);

EJS View:

<!-- Wappler include head-page="layouts/dashboard_v2" fontawesome_5="cdn" bootstrap5="local" is="dmx-app" id="testSendForm" appConnect="local" components="{dmxStateManagement:{},dmxBootstrap5TableGenerator:{},dmxBootstrap5PagingGenerator:{},dmxBootbox5:{}}" -->
<dmx-query-manager id="query1"></dmx-query-manager>
<meta name="ac:route" content="/testSendForm">
<dmx-serverconnect id="sc_testTablePopulate" url="/api/testPopulateForm" dmx-param:offset="query1.data.offset" dmx-param:limit="20" dmx-param:sort="query1.data.sort" dmx-param:dir="query1.data.dir"></dmx-serverconnect>

<div class="container">
  <div class="row">
    <div class="table-responsive">
      <table class="table table-striped table-hover">
        <thead>
          <tr>
            <th class="sorting" dmx-on:click="query1.set('sort','location_id');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='location_id' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='location_id' && query1.data.dir == 'desc'">Location</th>
            <th class="sorting" dmx-on:click="query1.set('sort','street_address');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='street_address' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='street_address' && query1.data.dir == 'desc'">Street address</th>
            <th class="sorting" dmx-on:click="query1.set('sort','postal_code');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='postal_code' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='postal_code' && query1.data.dir == 'desc'">Postal code</th>
            <th class="sorting" dmx-on:click="query1.set('sort','city');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='city' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='city' && query1.data.dir == 'desc'">City</th>
            <th class="sorting" dmx-on:click="query1.set('sort','state_province');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='state_province' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='state_province' && query1.data.dir == 'desc'">State province</th>
            <th class="sorting" dmx-on:click="query1.set('sort','country_id');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='country_id' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='country_id' && query1.data.dir == 'desc'">Country</th>
            <th class="sorting" dmx-on:click="query1.set('sort','null');query1.set('dir',query1.data.dir == 'desc' ? 'asc' : 'desc')" dmx-class:sorting_asc="query1.data.sort=='null' && query1.data.dir == 'asc'" dmx-class:sorting_desc="query1.data.sort=='null' && query1.data.dir == 'desc'">Action</th>
          </tr>
        </thead>
        <tbody is="dmx-repeat" dmx-generator="bs5table" dmx-bind:repeat="sc_testTablePopulate.data.query.data" id="tableRepeat1" dmx-state="query1" dmx-sort="sort" dmx-order="dir">
          <tr>
            <td dmx-text="location_id"></td>
            <td dmx-text="street_address"></td>
            <td dmx-text="postal_code"></td>
            <td dmx-text="city"></td>
            <td dmx-text="state_province"></td>
            <td dmx-text="country_id"></td>
            <td>
              <form id="form1" is="dmx-serverconnect-form" method="post" action="/api/testUpdateRow" dmx-on:success="sc_testTablePopulate.load({});notifies1.success('Successful removed')">
                <input id="text1" name="location_id" type="hidden" class="form-control" dmx-bind:value="location_id">
                <button id="btn1" class="btn btn-primary" dmx-on:click="run({'bootbox.confirm':{message:'Remove row?',then:{steps:{run:{outputType:'text',action:`form1.submit(true)`}}}}})">Confirm</button>
              </form>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
  <ul class="pagination justify-content-center" dmx-populate="sc_testTablePopulate.data.query" dmx-state="query1" dmx-offset="offset" dmx-generator="bs5paging">
    <li class="page-item" dmx-class:disabled="sc_testTablePopulate.data.query.page.current == 1" aria-label="First">
      <a href="javascript:void(0)" class="page-link" dmx-on:click="query1.set('offset',sc_testTablePopulate.data.query.page.offset.first)"><span aria-hidden="true">&lsaquo;&lsaquo;</span></a>
    </li>
    <li class="page-item" dmx-class:disabled="sc_testTablePopulate.data.query.page.current == 1" aria-label="Previous">
      <a href="javascript:void(0)" class="page-link" dmx-on:click="query1.set('offset',sc_testTablePopulate.data.query.page.offset.prev)"><span aria-hidden="true">&lsaquo;</span></a>
    </li>
    <li class="page-item" dmx-class:active="title == sc_testTablePopulate.data.query.page.current" dmx-class:disabled="!active" dmx-repeat="sc_testTablePopulate.data.query.getServerConnectPagination(2,1,'...')">
      <a href="javascript:void(0)" class="page-link" dmx-on:click="query1.set('offset',(page-1)*sc_testTablePopulate.data.query.limit)">{{title}}</a>
    </li>
    <li class="page-item" dmx-class:disabled="sc_testTablePopulate.data.query.page.current ==  sc_testTablePopulate.data.query.page.total" aria-label="Next">
      <a href="javascript:void(0)" class="page-link" dmx-on:click="query1.set('offset',sc_testTablePopulate.data.query.page.offset.next)"><span aria-hidden="true">&rsaquo;</span></a>
    </li>
    <li class="page-item" dmx-class:disabled="sc_testTablePopulate.data.query.page.current ==  sc_testTablePopulate.data.query.page.total" aria-label="Last">
      <a href="javascript:void(0)" class="page-link" dmx-on:click="query1.set('offset',sc_testTablePopulate.data.query.page.offset.last)"><span aria-hidden="true">&rsaquo;&rsaquo;</span></a>
    </li>
  </ul>
</div>

API testPopulateForm.json

{
  "meta": {
    "$_GET": [
      {
        "type": "text",
        "name": "sort"
      },
      {
        "type": "text",
        "name": "dir"
      },
      {
        "type": "text",
        "name": "offset"
      },
      {
        "type": "text",
        "name": "limit"
      }
    ]
  },
  "exec": {
    "steps": {
      "name": "query",
      "module": "dbconnector",
      "action": "paged",
      "options": {
        "connection": "db",
        "sql": {
          "type": "select",
          "columns": [
            {
              "table": "locations",
              "column": "location_id"
            },
            {
              "table": "locations",
              "column": "street_address"
            },
            {
              "table": "locations",
              "column": "postal_code"
            },
            {
              "table": "locations",
              "column": "city"
            },
            {
              "table": "locations",
              "column": "state_province"
            },
            {
              "table": "locations",
              "column": "country_id"
            }
          ],
          "params": [
            {
              "operator": "equal",
              "type": "expression",
              "name": ":P1",
              "value": "{{0}}",
              "test": ""
            }
          ],
          "table": {
            "name": "locations"
          },
          "joins": [],
          "primary": "location_id",
          "query": "select `location_id`, `street_address`, `postal_code`, `city`, `state_province`, `country_id` from `locations` where `locations`.`state` = ?",
          "wheres": {
            "condition": "AND",
            "rules": [
              {
                "id": "locations.state",
                "field": "locations.state",
                "type": "boolean",
                "operator": "equal",
                "value": "{{0}}",
                "data": {
                  "table": "locations",
                  "column": "state",
                  "type": "boolean",
                  "columnObj": {
                    "type": "boolean",
                    "default": "0",
                    "primary": false,
                    "nullable": false,
                    "name": "state"
                  }
                },
                "operation": "="
              }
            ],
            "conditional": null,
            "valid": true
          }
        }
      },
      "output": true,
      "meta": [
        {
          "name": "offset",
          "type": "number"
        },
        {
          "name": "limit",
          "type": "number"
        },
        {
          "name": "total",
          "type": "number"
        },
        {
          "name": "page",
          "type": "object",
          "sub": [
            {
              "name": "offset",
              "type": "object",
              "sub": [
                {
                  "name": "first",
                  "type": "number"
                },
                {
                  "name": "prev",
                  "type": "number"
                },
                {
                  "name": "next",
                  "type": "number"
                },
                {
                  "name": "last",
                  "type": "number"
                }
              ]
            },
            {
              "name": "current",
              "type": "number"
            },
            {
              "name": "total",
              "type": "number"
            }
          ]
        },
        {
          "name": "data",
          "type": "array",
          "sub": [
            {
              "type": "number",
              "name": "location_id"
            },
            {
              "type": "text",
              "name": "street_address"
            },
            {
              "type": "text",
              "name": "postal_code"
            },
            {
              "type": "text",
              "name": "city"
            },
            {
              "type": "text",
              "name": "state_province"
            },
            {
              "type": "text",
              "name": "country_id"
            }
          ]
        }
      ],
      "outputType": "object",
      "type": "dbconnector_paged_select"
    }
  }
}

API testUpdateRow.json

{
  "meta": {
    "$_POST": [
      {
        "type": "number",
        "name": "location_id"
      }
    ]
  },
  "exec": {
    "steps": {
      "name": "",
      "module": "core",
      "action": "condition",
      "options": {
        "if": "{{$_POST.location_id}}",
        "then": {
          "steps": {
            "name": "update",
            "module": "dbupdater",
            "action": "update",
            "options": {
              "connection": "db",
              "sql": {
                "type": "update",
                "values": [
                  {
                    "table": "locations",
                    "column": "state",
                    "type": "boolean",
                    "value": "{{1}}"
                  }
                ],
                "table": "locations",
                "wheres": {
                  "condition": "AND",
                  "rules": [
                    {
                      "id": "location_id",
                      "type": "double",
                      "operator": "equal",
                      "value": "{{$_POST.location_id}}",
                      "data": {
                        "column": "location_id"
                      },
                      "operation": "="
                    }
                  ]
                },
                "returning": "location_id",
                "query": "update `locations` set `state` = ? where `location_id` = ?",
                "params": [
                  {
                    "name": ":P1",
                    "type": "expression",
                    "value": "{{1}}",
                    "test": ""
                  },
                  {
                    "operator": "equal",
                    "type": "expression",
                    "name": ":P2",
                    "value": "{{$_POST.location_id}}",
                    "test": ""
                  }
                ]
              }
            },
            "meta": [
              {
                "name": "affected",
                "type": "number"
              }
            ]
          }
        },
        "else": {
          "steps": {
            "name": "error400",
            "module": "core",
            "action": "response",
            "options": {
              "status": 400,
              "data": "NoData"
            }
          }
        }
      },
      "outputType": "boolean"
    }
  }
}

Now, just clic on Confirm button on the Action column in table, the first time bootbox show up, then clic con Ok button and the table update correctly (Dev tab show XHR of the form updated).


Now, clic on any other Confirm button, the bootbox show up but the OK button now have not any action, no XHR of this new submit form:

If I remove sc_testTablePopulate.load({}) from form1 on dmx-on:success the bootbox a second time works normally, but of course this is not a option because sc_testTablePopulate.load({}) it is need it to update data from Table.

If you need anything else, please let me know.
Thank you.

Hi, any news on this?
Thanks

Might be related to:

Do you have select inputs in your form? I can't replicate it but maybe it's connected?

Try adding a key to the repeater, on the tbody add key="location_id".

Hi @patrick, I've tried this before, but when Teodor told me, I was adding a text value and not a databinding value to use as unique key, that was the problem, but as I told Teodor, in previous versions this was not needed, I mean v6.8, I guess it's because of the many optimizations the new version of Wappler has surfed, so should this be added here and by default in all new projects?

Thanks

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.