I have been having an issue with my app when sending fetch requests on iOS devices (only in Safari). When attempting to send a fetch request I get the Load failed error which seems to be a network error. As mentioned this only happens when using Safari on iOS devices and even then it works on some devices and not on others.
Please see my full controller below:
`import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static get targets() {
return ["question", "submit", "next", "back", "source", "preview", "error", "loading"];
}
static get values() {
return { responseDetailsPath: String };
}
initialize() {
console.log("Questionnaire controller initialized test for 24/06/2024")
this.showCurrentQuestion(0);
this.lastSelectedImages = {};
this.hasUnsavedChanges = false;
this.initInputListeners();
}
Hope you got it sorted out, but issues opened with this kind of code dump isn't likely to get much attention. You'll have to produce a minimally viable test case to get someone to investigate stuff like this.
Hello everyone,
I have been having an issue with my app when sending fetch requests on iOS devices (only in Safari). When attempting to send a fetch request I get the Load failed error which seems to be a network error. As mentioned this only happens when using Safari on iOS devices and even then it works on some devices and not on others.
Please see my full controller below:
`import { Controller } from "@hotwired/stimulus"
export default class extends Controller { static get targets() { return ["question", "submit", "next", "back", "source", "preview", "error", "loading"]; }
static get values() { return { responseDetailsPath: String }; }
initialize() { console.log("Questionnaire controller initialized test for 24/06/2024") this.showCurrentQuestion(0); this.lastSelectedImages = {}; this.hasUnsavedChanges = false; this.initInputListeners(); }
initInputListeners() { this.questionTargets.forEach((question) => { const inputs = question.querySelectorAll("input, select, textarea"); inputs.forEach((input) => { input.addEventListener("change", () => { this.hasUnsavedChanges = true; }); }); }); }
displayLoadingAnimation(show) { if (show) { this.loadingTarget.classList.remove("d-none"); } else { this.loadingTarget.classList.add("d-none"); } }
async next() { if (!this.validateResponse()) { return; } if (this.hasUnsavedChanges) { this.displayLoadingAnimation(true); try { await this.sendResponse(); } catch (error) { this.displayLoadingAnimation(false); return; } } const currentIndex = this.currentQuestionIndex(); if (currentIndex < this.questionTargets.length - 1) { this.showCurrentQuestion(currentIndex + 1); } this.updateButtonVisibility(); }
async previous() { const currentIndex = this.currentQuestionIndex();
}
updateButtonVisibility() { const currentIndex = this.currentQuestionIndex(); this.backTarget.classList.toggle("d-none", currentIndex === 0); this.nextTarget.classList.toggle("d-none", currentIndex === this.questionTargets.length - 1); this.submitTarget.classList.toggle("d-none", currentIndex !== this.questionTargets.length - 1); }
validateResponse() { const currentIndex = this.currentQuestionIndex(); const currentQuestion = this.questionTargets[currentIndex]; const input = currentQuestion.querySelector("input, select, textarea"); const isRequired = input.required; const inputType = input.getAttribute("type"); let isValid;
}
showCurrentQuestion(index) { this.questionTargets.forEach((element, i) => { element.classList.toggle("d-none", i !== index); const errorMessageDiv = this.errorTargets[i]; if (errorMessageDiv) { errorMessageDiv.style.display = "none"; } if (i === index && element.dataset.existingImage) { this.updateImagePreview(element); } }); this.hasUnsavedChanges = false; }
updateImagePreview(questionElement) { const existingImageUrl = questionElement.dataset.existingImage; const previewTarget = questionElement.querySelector("[data-questionnaire-target='preview"); if (existingImageUrl) { previewTarget.innerHTML =
<img src="${existingImageUrl}">
; } else { previewTarget.innerHTML = ''; } }currentQuestionIndex() { return this.questionTargets.findIndex((element) => !element.classList.contains("d-none")); }
async show(event) { const index = this.sourceTargets.indexOf(event.target); const previewTarget = this.previewTargets[index]; const file = event.target.files[0]; const currentIndex = this.currentQuestionIndex(); const currentQuestion = this.questionTargets[currentIndex];
}
async submit(event) { event.preventDefault(); if (!this.validateResponse()) { return; } this.displayLoadingAnimation(true);
}
async sendResponse(isFinal = false) { const currentIndex = this.currentQuestionIndex(); const currentQuestion = this.questionTargets[currentIndex]; const input = currentQuestion.querySelector("input, select, textarea"); const name = input.name; const value = input.value;
}
fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function (e) { resolve(e.target.result); }; reader.onerror = function (error) { reject(error); }; reader.readAsDataURL(file); }); }
displayErrorMessage(questionElement, message) { const errorMessageDiv = questionElement.querySelector("[data-questionnaire-target='error']"); if (errorMessageDiv) { errorMessageDiv.style.display = "block"; errorMessageDiv.textContent = message; } }
findQuestionIndex(input) { let parentQuestion = input.closest("[data-questionnaire-target='question']"); return this.questionTargets.indexOf(parentQuestion); }
resizeImage(file, maxWidth = 1024, maxHeight = 1024, quality = 1) { return new Promise((resolve, reject) => { const img = document.createElement("img"); const reader = new FileReader();
} } `