Hello Retool Community,
I am currently working on a project where I have developed a custom component in Retool. This component is a numeric input field that allows users to input a number, which gets updated in the Retool model. I have successfully implemented and styled this component, and it's working great.
However, I've come across a challenge that I'm hoping to get some insights on. I need to use multiple instances of this custom component across different parts of my application. Ideally, I want to maintain a single codebase for this component to ensure consistency and ease of maintenance.
Here’s a brief overview of my component:
- It’s an HTML input of type number.
- Includes JavaScript for debouncing input changes and updating the Retool model.
- Styled with CSS to fill the entire frame of the component.
I am considering a few options, like using JavaScript snippets or creating an external JavaScript module, but I'm not sure what the best approach would be in Retool. My main goal is to avoid duplicating the same HTML and JavaScript code for each instance of the component.
Questions:
- Does Retool support creating templates or reusable components that can be instantiated multiple times with a single codebase?
- If not, what are the recommended best practices for handling such scenarios? Would JavaScript snippets be the way to go, or is there a more efficient method?
- Are there any potential pitfalls or considerations I should be aware of when reusing component code in Retool?
Any advice, tips, or examples from your own experience would be greatly appreciated. I'm looking to implement a solution that is both efficient and maintainable.
Thank you in advance for your help!
Best regards,
heres the frame code
html, body { margin: 0; padding: 0; height: 100%; width: 100%; } #stepper { display: flex; align-items: center; height: 100%; width: 100%; justify-content: center; } input[type="number"] { width: 100%; height: 100%; border: none; padding: 0; margin: 0; }<div id="stepper">
<input id="numberInput" type="number" step="0.25" oninput="handleInputChange(event)" style="text-align: center;" />
</div>
<script>
let currentNumber = 0;
function debounce(func, delay) {
let debounceTimer;
return function() {
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
}
}
function updateDisplay() {
document.getElementById("numberInput").value = currentNumber.toFixed(2);
window.Retool.modelUpdate({ number: currentNumber });
}
function handleInputChange(event) {
const newValue = parseFloat(event.target.value);
if (!isNaN(newValue)) {
currentNumber = newValue;
debouncedUpdateDisplay();
}
}
const debouncedUpdateDisplay = debounce(updateDisplay, 250); // 250 ms delay
window.Retool.subscribe(function (model) {
currentNumber = model.number || 0;
debouncedUpdateDisplay();
});
// Initialize display
debouncedUpdateDisplay();
</script>
body:
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
#stepper {
display: flex;
align-items: center;
height: 100%;
width: 100%;
justify-content: center;
}
input[type="number"] {
width: 100%;
height: 100%;
border: none;
padding: 0;
margin: 0;
}
<div id="stepper">
<input id="numberInput" type="number" step="0.25" oninput="handleInputChange(event)" style="text-align: center;" />
</div>
<script>
let currentNumber = 0;
function debounce(func, delay) {
let debounceTimer;
return function() {
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
}
}
function updateDisplay() {
document.getElementById("numberInput").value = currentNumber.toFixed(2);
window.Retool.modelUpdate({ number: currentNumber });
}
function handleInputChange(event) {
const newValue = parseFloat(event.target.value);
if (!isNaN(newValue)) {
currentNumber = newValue;
debouncedUpdateDisplay();
}
}
const debouncedUpdateDisplay = debounce(updateDisplay, 250); // 250 ms delay
window.Retool.subscribe(function (model) {
currentNumber = model.number || 0;
debouncedUpdateDisplay();
});
// Initialize display
debouncedUpdateDisplay();
</script>