Open zackdutra opened 2 months ago
If I am incorrectly using HTMX to load additional fields into my form, please let me know what the proper way to format this is.
Couple of thoughts on this:
^/test/second-endpoint
. Would seem to make more sense or have the 'loading' logic in the actual content load on the block. Hey Jon, thanks for those thoughts. The actual endpoints I'm using have quite a bit more content, this is just a simplified example. In the real version of the second endpoint, I have the content below as an image file upload section of the form. It's unfinished and a little sloppy - I only got through some basic setup when I hit this issue. I post the full content just to give you an idea of the length of content. Ideally I'd be able to load in this field set separately because of how much content there is.
Also in the real world use case, the first endpoint that I mentioned is only exposed after clicking on "edit", after which you get the name input and the image uploads.
I'm open to your feedback on how to best handle that.
{% comment %}Set file type Id here.{% endcomment %}
{% assign fileTypeId = 3 %}
{% comment %}Load existing artwork from attributes.{% endcomment %}
{% assign eventItemId = QueryString.eventitemid %}
{% eventitem Id:'{{ eventItemId }}' %}{% endeventitem %}
{% if eventitem %}
{% assign artworkSquare = eventitem | Attribute:'ArtworkSquare' %}
{% assign artworkWide = eventitem | Attribute:'ArtworkWide' %}
{% assign artworkBanner = eventitem | Attribute:'ArtworkBanner' %}
{% endif %}
{% capture defaultImageSvg %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.3 1A2.7 2.7 0 0 1 19 3.7v12.6a2.7 2.7 0 0 1-2.7 2.7H3.7A2.7 2.7 0 0 1 1 16.3V3.7A2.7 2.7 0 0 1 3.7 1h12.6zm-7.708 9.83l-5.466 4.192a1.12 1.12 0 0 1-.125.082L3 16.3a.7.7 0 0 0 .7.7h12.6a.7.7 0 0 0 .7-.7v-2.281l-.033-.022-1.976-1.527-2.045 1.535a1 1 0 0 1-1.22-.014l-.091-.083-3.043-3.078zM16.3 3H3.7a.7.7 0 0 0-.7.7v8.897l5.074-3.89a1 1 0 0 1 1.227.007l.093.083 3.05 3.085 1.956-1.469a1 1 0 0 1 1.1-.066l.111.075L17 11.495V3.7a.7.7 0 0 0-.7-.7zm-2.8 3a1.5 1.5 0 1 1-.001 3.001A1.5 1.5 0 0 1 13.5 6z"></path>
</svg>
{% endcapture %}
{% comment %}Define JSON data for uploaders{% endcomment %}
{% assign uploadersJson = '[
{ "id": "1", "width": "1024", "height": "1024", "aspectRatio": "1-1" },
{ "id": "2", "width": "1920", "height": "1080", "aspectRatio": "16-9" },
{ "id": "3", "width": "1920", "height": "692", "aspectRatio": "1920-692" }
]' %}
{% assign uploaders = uploadersJson | FromJSON %}
{% comment %}Render each uploader using the JSON data{% endcomment %}
{% for uploader in uploaders %}
<div class="drop-zone" data-width="{{ uploader.width }}" data-height="{{ uploader.height }}" onclick="document.getElementById('fileInput{{ uploader.id }}').click();">
<div id="previewContainer{{ uploader.id }}" class="preview-container aspect-ratio-{{ uploader.aspectRatio }}">
{{ defaultImageSvg }}
</div>
<input type="file" id="fileInput{{ uploader.id }}" class="input-button-hidden" hidden onchange="uploadFile(this, 'previewContainer{{ uploader.id }}')">
<p>
<span><strong>Drag and drop your file here or click to select a file.</strong></span><br>
<span>{{ uploader.width }} x {{ uploader.height }} • 15 MB maximum</span><br>
<span>Supported: PNG, JPG</span>
</p>
</div>
{% endfor %}
<script>
function uploadFile(fileInputElement, previewContainerId) {
var fileInput = fileInputElement;
var file = fileInput.files[0];
var dropZone = fileInput.closest('.drop-zone');
var requiredWidth = parseInt(dropZone.getAttribute('data-width'));
var requiredHeight = parseInt(dropZone.getAttribute('data-height'));
// Check if the file is an image
if (file && file.type.match('image.*')) {
var img = new Image();
img.onload = function() {
// Check the image dimensions
if (img.width === requiredWidth && img.height === requiredHeight) {
// Proceed to upload
var formData = new FormData();
formData.append('file', file);
var binaryFileTypeId = {{ fileTypeId }};
fetch(`/api/BinaryFiles/Upload?binaryFileTypeId=${binaryFileTypeId}`, {
method: 'POST',
body: formData,
headers: { 'Accept': 'application/json' }
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
var reader = new FileReader();
reader.onload = function (e) {
var previewContainer = document.getElementById(previewContainerId);
var imgElement = previewContainer.querySelector('img.preview-image');
if (!imgElement) {
imgElement = document.createElement('img');
imgElement.classList.add('preview-image');
previewContainer.innerHTML = ''; // Clear the SVG icon
previewContainer.appendChild(imgElement);
}
imgElement.src = e.target.result;
};
reader.readAsDataURL(file);
})
.catch(error => {
console.error('Error:', error);
var previewContainer = document.getElementById(previewContainerId);
var imgElement = previewContainer.querySelector('img.preview-image');
if (imgElement) {
imgElement.remove(); // Remove the image if the upload fails
}
alert('Failed to upload the image.');
});
} else {
// Image dimensions do not match
alert(`Image must be exactly ${requiredWidth} x ${requiredHeight} pixels.`);
// Clear the file input
fileInput.value = '';
}
};
img.onerror = function() {
alert('Invalid image file.');
fileInput.value = '';
};
// Read the file as Data URL
img.src = URL.createObjectURL(file);
} else {
alert('Please select a valid image file.');
fileInput.value = '';
}
}
// Adding Drag and Drop functionality
var dropZones = document.querySelectorAll('.drop-zone');
dropZones.forEach(function(dropZone) {
dropZone.addEventListener('dragover', function(e) {
e.preventDefault();
dropZone.classList.add('active');
});
dropZone.addEventListener('dragleave', function(e) {
dropZone.classList.remove('active');
});
dropZone.addEventListener('drop', function(e) {
e.preventDefault();
dropZone.classList.remove('active');
var files = e.dataTransfer.files;
if (files.length) {
// Find the corresponding file input
var fileInput = dropZone.querySelector('input[type="file"]');
fileInput.files = files;
// Trigger the upload function
uploadFile(fileInput, dropZone.querySelector('.preview-container').id);
}
});
});
</script>
When loaded correctly with the form tag, the real use case looks like the form below.
Description
When a lava-form has an htmx element inside of it as well as a text input lava shortcode, validation is triggered on the initial load for the input, and that nested htmx is never rendered.
Actual Behavior
When a lava-form has an htmx element inside of it as well as a text input lava shortcode, validation is triggered on the initial load for the input, and that nested htmx is never rendered. Replacing with
Expected Behavior
Validation on the text input should only occur when the form is submitted.
Steps to Reproduce
Create a new lava application with a slug of test
Create an endpoint with a slug of form, HTTP method of GET, and Security Mode of Application View. Set the Code Template to:
Create another endpoint with a slug of second-endpoint, HTTP method of GET, and Security Mode of Application View. Set the Code template to anything, such as "content goes here"
Create a page with a Lava Application Content block. Set the application to the one you just created and set the content to:
On saving and reloading the page, you should see the rendered content below, as well as a successful network request to the form endpoint
Go back to the form endpoint and change the lava-form opening and closing tags to regular HTML form closing tags, such as below:
Notice the page now loads as expected, with network requests to both of our endpoints.
Issue Confirmation
Rock Version
16.6