Troubleshooting the updateModel() method

Hi there!
I'm having trouble with the updating of the model state within a custom component that uses plain JS.

What's blocking me is that the update works when I fill the form but not when I want to remove information from that model data structure. I've already tried looking for answers in the forum but couldn't find anything that would work with my case.

Here's the code for my custom component:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <style>
        /* Style for the custom component container */
        .custom-component {
            margin-bottom: 16px;
            display: flex;
            flex-direction: column;
            height: auto;
            max-width: 600px;
        }
        .input-field-container {
            margin-bottom: 8px;
            display: flex;
            align-items: center;
        }
        .input-field {
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            margin-right: 8px;
            flex: 1;
        }
        .remove-button {
            padding: 8px 16px;
            background-color: #f44336;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .add-button {
            padding: 0px 16px;
            background-color: transparent;
            color: #3b82f6;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            align-self: left;
            font-size: 14px;
        }
        .add-button-container {
            display: flex;
            justify-content: start;
        }
    </style>
   
    <div class="custom-component">
        <!-- Initial input field -->
        <div class="input-field-container" id="inputContainer_0">
            <input id="input_0_description" class="input-field" type="text" placeholder="Product Description" onkeyup="updateDescription(0, event)" />
            <input id="input_0_amount" class="input-field" type="number" placeholder="Amount" onkeyup="updateAmount(0, event)" />
            <button class="remove-button" onclick="removeInputField(0)">Remove</button>
        </div>
    </div>

    <div class="add-button-container">
        <button class="add-button" onclick="addInputField()">+ Add product or service</button>
    </div>
   
    <script>
      var model = []
      function updateDescription(index, e) {
        model[index] = { ...model[index], description: e.target.value }; // Update description for the corresponding index
        window.Retool.modelUpdate(model);
      }
     
      function updateAmount(index, e) {
        model[index] = { ...model[index], amount: parseInt(e.target.value) }; // Update amount for the corresponding index
        window.Retool.modelUpdate(model);
      }

      function removeInputField(index) {
            // var currentModel = [...model];
            var containerId = 'inputContainer_' + index;
            var inputFieldContainer = document.getElementById(containerId);
            if (inputFieldContainer) {
                inputFieldContainer.parentNode.removeChild(inputFieldContainer);
            }
            model.splice(index, 1);
            console.log('Updated model before Retool update:', model); // Log updated model before Retool update
            window.Retool.modelUpdate(model);
            // console.log('Model after Retool update:', model); // Log updated model after Retool update
      }
     
      function addInputField() {
            var currentModel = [...window.model];
            var customComponent = document.querySelector('.custom-component');
            var index = currentModel.length; // Get the index for the new input field

            var inputFieldContainer = document.createElement('div');
            inputFieldContainer.classList.add('input-field-container');

            var descriptionInput = document.createElement('input');
            descriptionInput.classList.add('input-field');
            descriptionInput.type = 'text';
            descriptionInput.placeholder = 'Product Description';
            descriptionInput.id = 'input_' + index + '_description';
            descriptionInput.onkeyup = function(e) { updateDescription(index, e); };

            var amountInput = document.createElement('input');
            amountInput.classList.add('input-field');
            amountInput.type = 'number';
            amountInput.placeholder = 'Amount';
            amountInput.id = 'input_' + index + '_amount';
            amountInput.onkeyup = function(e) { updateAmount(index, e); };

            var removeButton = document.createElement('button');
            removeButton.classList.add('remove-button');
            removeButton.textContent = 'Remove';
            removeButton.onclick = function() {
                customComponent.removeChild(inputFieldContainer);
                removeInputField(index); // Remove corresponding data from the model
            };

            inputFieldContainer.appendChild(descriptionInput);
            inputFieldContainer.appendChild(amountInput);
            inputFieldContainer.appendChild(removeButton);

            customComponent.appendChild(inputFieldContainer);

            model.push({ description: '', amount: 0 });

            console.log(model); // Log the updated model
        }

             
      window.Retool.subscribe(function (newModel) {
        // Subscribes to model updates. All model values can be accessed here
        console.log('Model:', newModel);
      });
    </script>
</body>
</html>

I need to access the model value from a workflow query. The removeInputField method is not removing the object from the custom component model.

Any guidance with this would be much appreciated! Thanks in advance.

Hi @franco_p

you cannot remove a property that belong to the model in Retool.

The modelUpdate works in a way that is updating only the properties you're passing to. Removing a prop from the object you're passing to modelUpdate just makes Retool ignoring that property for the updating action.

A workaround would be to assign a special value, i.e. __remove__ that you can then handle in Retool, before consume that object.

Hope this help.

1 Like

Hi @abusedmedia !

That was really helpful, I managed to use that workaround. Thank you so much!

1 Like

Hi there @abusedmedia , hope you're well. I'm getting back on this thread as I am experiencing some trouble with setting the model back to its initial state. Is there a way to do this? Or maybe forcing a reload on the custom component so that it collaterally resets the model. I've spent some time on reading other threads but couldn't find a solution fit for this.

Thanks in advance!

Hi,

to reset the model, meaning you want the initial value, without reloading the app, you have to do explicitly by setting it in someway, both from Retool as well as from the Component.

Hope this help