На своей работе я пытаюсь придерживаться определённых принципов разработки программного обеспечения. Одним из них является минимизация написания кода и создание универсальный классов и функций. Именно этот принцип предполагал использование jquery.unobtrusive-ajax.js и класс Ajax для mvc.
В этой статье нас интересует непосредственно Ajax.BeginForm.
Пример:
@using (Ajax.BeginForm("UploadAvatarImage", "Dashboard", null, new AjaxOptions {HttpMethod = "POST", OnSuccess = "UpdateAvatars()", OnFailure = "document.refresh()" }, new {Id = "UploadAvatartForm", enctype = "multipart/form-data" })) { <div class="input-group pt-1 pl-1"> <input type="file" name="avatarFile" class="form-control" id="fileUploaderControl" accept=".jpg"/> </div> <button class="btn btn-sm btn-primary btn-block m-1" type="submit" >Изменить</button> }
Столкнувшись с тем, что Ajax.BeginForm не передает input[type=file], я провел глобальный поиск по нахождению решения данной проблема. Старый любимый stackoverflow на каждом решение предлагал одно и то же. Не использовать Ajax.BeginForm, использовать FormData, сделать php обработчик и еще тучи советов по увеличению программного кода.
Как я говорил ранее, в своих продуктах я придерживаюсь принципов и раз ответ, который бы удовлетворял мои требования, не был найден, пришлось всё делать самому.
Первое, что потребовалось, это определить какая функция в jquery.unobtrusive-ajax.js отвечает за формирование data.
$(document).on("submit", "form[data-ajax=true]", function (evt) { var clickInfo = $(this).data(data_click) || [], clickTarget = $(this).data(data_target), isCancel = clickTarget && (clickTarget.hasClass("cancel") || clickTarget.attr('formnovalidate') !== undefined); evt.preventDefault(); if (!isCancel && !validate(this)) { return; } asyncRequest(this, { url: this.action, type: this.method || "GET", // То что нас интересует data: clickInfo.concat($(this).serializeArray()) }); });
Нас интересует функция $(this).serializeArray(). В переменной $(this) приходит наша форма <form/> и затем сереализуется в массив. Протестировав её в консоли, а так же её аналог serialize() было определено, что данные функции не загружают файлы в принципе. Отсюда и решение переписать её.
Но для начала надо решить проблему с загрузкой файла, для этого воспользуемся класс FileReader.
$(document).on("change", "form[data-ajax=true] input[type=file]", function (evt) { var form = $($(evt.target).parents("form")[0]); if (evt.target.files.length > 0) { var fileObj = evt.target.files[0]; var reader = new FileReader(); reader.onload = function () { evt.target.setAttribute("data-ajax-image-data", reader.result); form.find("button[type=submit]")[0].classList.remove("disabled"); } reader.onerror = function () { console.log("Error while loading"); form.find("button[type=submit]")[0].classList.remove("disabled"); } form.find("button[type=submit]")[0].classList.add("disabled"); reader.readAsDataURL(fileObj); } });
Немного о коде.
Сначала привязываем событие изменение input-а, выбор пал на «change».
Затем проверяем, выбран ли файл.
Если файл выбран то начинаем его загрузку. Проблема состояла в том, что данные надо где то хранить, в примере мы запишем данные в атрибут самого объекта.
Блокируем кнопку подтверждения до момента загрузки файла.
В случаи ошибки, пишем это в консоль. Это не окончательный вариант функции, каждый может модернизировать её под свои нужды.
После того, как мы создали загрузку данных файла, переходим к модернизации сериализации.
function selfFormSerializeArray(form) { var array = form.serializeArray(); for (var i = 0; i < form[0].length; i++) { if (form[0][i].type === "file") { var fileObj = form[0][i]; var data = fileObj.getAttribute("data-ajax-image-data") || ""; if (data !== "") { array.push({ name: fileObj.name, value: data }); // console.log("SUCCESS"); } else { //console.log("ERROR"); } } } return array; }
И используем эту функцию в основной функции.
$(document).on("submit", "form[data-ajax=true]", function (evt) { var clickInfo = $(this).data(data_click) || [], clickTarget = $(this).data(data_target), isCancel = clickTarget && (clickTarget.hasClass("cancel") || clickTarget.attr('formnovalidate') !== undefined); evt.preventDefault(); if (!isCancel && !validate(this)) { return; } asyncRequest(this, { url: this.action, type: this.method || "GET", data: clickInfo.concat(selfFormSerializeArray($(this))) // clickInfo.concat($(this).serializeArray()) }); });
По завершению требуется дописать обработчик и парсер данных. На скорую руку это выглядет приблизительно так.
Обработчик
public PartialViewResult UploadAvatarImage() { if (!ImageHelper.LoadAvatarImage(this.Request.Form["avatarFile"])) { return null; } return PartialView("Null"); }
Хелпер
public static bool LoadAvatarImage(string data) { try { var file = AjaxFile.Create(data); return true; } catch (Exception ex) { return false; }
Парсер
public AjaxFile(string data) { var infos = data.Split(','); this.Type = infos[0].Split(';')[0].Split(':')[1].Split('/')[1]; this.Source = Convert.FromBase64String(infos[1]); ; } public static AjaxFile Create(string data) { return new AjaxFile(data); }
Поздравляю! Теперь вы можете загружать файлы использую стандартный Ajax для mvc.
ссылка на оригинал статьи https://habr.com/post/423383/
Добавить комментарий