0

I am working on ASP.NET Core project and in modal I have a form where I need to upload a file. I am also using unobtrusive AJAX to submit the form. At first I was not able to upload the file, so I fave found this solution:

Upload is null after adding jquery.unobtrusive-ajax.js reference

I have added data-ext="true" in the form. My form is in modal and the modal is showing when I click a button:

<form asp-area="@Areas.ObserverArea" asp-controller="@WebControllers.BrandController" asp-action="@ObserverActions.SaveBrand" enctype="multipart/form-data" method="POST" data-ext="true" data-ajax-begin="ajaxValidation" data-ajax-method="POST" data-ajax-mode="replace-with" data-ajax-update="#brandList" id="submit-form">

And the file is being uploaded, brand is created, etc, everything on the back-end is working fine, however the modal is not closing even if I click the close button, so in order to "remove" the modal I need to refresh the page. I have a few other places where unobtrusive AJAX and modals are working just fine - this is happening only I'm uploading file.

EDIT 1:

Here is the whole code of the modal:

<div class="modal" id="addBrand">
<form asp-area="@Areas.ObserverArea" asp-controller="@WebControllers.BrandController" asp-action="@ObserverActions.SaveBrand" enctype="multipart/form-data" method="POST" data-ajax="true" data-ajax-begin="ajaxValidation" data-ajax-method="POST" data-ajax-mode="replace-with" data-ajax-update="#brandList" id="submit-form">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title float-left">Add New Brand</h4>
                <button type="button" class="close float-right" data-dismiss="modal" aria-label="Close" onclick="$('#addBrand').hide()">
                    <span aria-hidden="true" class="float-right">×</span>
                </button>

            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label for="BrandName">Brand Name</label>
                    <input required class="form-control" placeholder="Enter Brand Name" name="BrandName" type="text">
                </div>
                <div class="form-group">
                    <label for="KPBrandId">Kinesis Brand Id</label>
                    <input required class="form-control" placeholder="Enter Kinesis Brand Id" name="KinesisBrandId" type="text">
                </div>
                <div class="form-group">
                    <label for="file">Brand Logo</label>
                    <input type="file" name="file">
                    <input type="hidden" name="test" value="33test" />
                </div>
            </div>
            <div class="modal-footer">
                @Html.HiddenFor(x => x.CategoryId)
                <button type="button" class="btn btn-default btn-flat pull-right btn-margin-5" onclick="$('#addBrand').hide()" data-dismiss="modal">Close</button>
                <button type="submit" id="saveButton" class="btn btn-primary btn-flat pull-right">Save</button>
            </div>
        </div>
        <!-- /.modal-content -->
    </div>
    <!-- /.modal-dialog -->
</form>

EDIT 2: Here is the code for unobtrusive ajax, after I have modified it according to the post I have shared. In the modal right now I don't have data-ext="true", but I have tested with that as well. One thing I forgot to mention - the unobtrusive ajax library I'm using was included in the Admin LTE template I have downloaded.

(function ($) {
    var data_click = "unobtrusiveAjaxClick",
        data_target = "unobtrusiveAjaxClickTarget",
        data_validation = "unobtrusiveValidation";

    function getFunction(code, argNames) {
        var fn = window, parts = (code || "").split(".");
        while (fn && parts.length) {
            fn = fn[parts.shift()];
        }
        if (typeof (fn) === "function") {
            return fn;
        }
        argNames.push(code);
        return Function.constructor.apply(null, argNames);
    }

    function isMethodProxySafe(method) {
        return method === "GET" || method === "POST";
    }

    function asyncOnBeforeSend(xhr, method) {
        if (!isMethodProxySafe(method)) {
            xhr.setRequestHeader("X-HTTP-Method-Override", method);
        }
    }

    function asyncOnSuccess(element, data, contentType) {
        var mode;

        if (contentType.indexOf("application/x-javascript") !== -1) {  // jQuery already executes JavaScript for us
            return;
        }

        mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
        $(element.getAttribute("data-ajax-update")).each(function (i, update) {
            var top;

            switch (mode) {
            case "BEFORE":
                top = update.firstChild;
                $("<div />").html(data).contents().each(function () {
                    update.insertBefore(this, top);
                });
                break;
            case "AFTER":
                $("<div />").html(data).contents().each(function () {
                    update.appendChild(this);
                });
                break;
            case "REPLACE-WITH":
                $(update).replaceWith(data);
                break;
            default:
                $(update).html(data);
                break;
            }
        });
    }

    function asyncRequest(element, options) {
        var confirm, loading, method, duration;

        confirm = element.getAttribute("data-ajax-confirm");
        if (confirm && !window.confirm(confirm)) {
            return;
        }

        loading = $(element.getAttribute("data-ajax-loading"));
        duration = parseInt(element.getAttribute("data-ajax-loading-duration"), 10) || 0;

        $.extend(options, {
            type: element.getAttribute("data-ajax-method") || undefined,
            url: element.getAttribute("data-ajax-url") || undefined,
            cache: !!element.getAttribute("data-ajax-cache"),
            beforeSend: function (xhr) {
                var result;
                asyncOnBeforeSend(xhr, method);
                result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(element, arguments);
                if (result !== false) {
                    loading.show(duration);
                }
                return result;
            },
            complete: function () {
                loading.hide(duration);
                getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(element, arguments);
            },
            success: function (data, status, xhr) {
                asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
                getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(element, arguments);
            },
            error: function () {
                getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"]).apply(element, arguments);
            }
        });

        options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

        method = options.type.toUpperCase();
        if (options.data instanceof FormData) {
            options.processData = false;
            options.contentType = false;
            options.data.append("X-Requested-With", "XMLHttpRequest");

            if (!isMethodProxySafe(method)) {
                options.type = "POST";
                options.data.append("X-HTTP-Method-Override", method);
            }
        } else {
            options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

            if (!isMethodProxySafe(method)) {
                options.type = "POST";
                options.data.push({ name: "X-HTTP-Method-Override", value: method });
            }
        }

        $.ajax(options);
    }

    function validate(form) {
        var validationInfo = $(form).data(data_validation);
        return !validationInfo || !validationInfo.validate || validationInfo.validate();
    }

    $(document).on("click", "a[data-ajax=true]", function (evt) {
        evt.preventDefault();
        asyncRequest(this, {
            url: this.href,
            type: "GET",
            data: []
        });
    });

    $(document).on("click", "form[data-ajax=true] input[type=image]", function (evt) {
        var name = evt.target.name,
            target = $(evt.target),
            form = $(target.parents("form")[0]),
            offset = target.offset();

        form.data(data_click, [
            { name: name + ".x", value: Math.round(evt.pageX - offset.left) },
            { name: name + ".y", value: Math.round(evt.pageY - offset.top) }
        ]);

        setTimeout(function () {
            form.removeData(data_click);
        }, 0);
    });

    $(document).on("click", "form[data-ajax=true] :submit", function (evt) {
        var name = evt.currentTarget.name,
            target = $(evt.target),
            form = $(target.parents("form")[0]);

        form.data(data_click, name ? [{ name: name, value: evt.currentTarget.value }] : []);
        form.data(data_target, target);

        setTimeout(function () {
            form.removeData(data_click);
            form.removeData(data_target);
        }, 0);
    });

    $(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");
        evt.preventDefault();
        if (!isCancel && !validate(this)) {
            return;
        }
        asyncRequest(this, {
            url: this.action,
            type: this.method || "GET",
            data: clickInfo.concat($(this).serializeArray())
        });
    });
    
    
    $(document).on("submit", "form[data-ext=true]", function (evt) {
        var clickInfo = $(this).data(data_click) || [],
            clickTarget = $(this).data(data_target),
            isCancel = clickTarget && clickTarget.hasClass("cancel");
        evt.preventDefault();
        if (!isCancel && !validate(this)) {
            return;
        }
        var formData;
        if (this.enctype && this.enctype === "multipart/form-data") {
            formData = new FormData(this);
        } else {
            formData = clickInfo.concat($(this).serializeArray());
        }

        asyncRequest(this, {
            url: this.action,
            type: this.method || "GET",
            data: formData
        });
    });
}(jQuery));
Ivan
  • 31
  • 2
  • 11
  • Hi @Ivan,could you please share enough code to reproduce the issue? – Rena Jun 07 '21 at 01:54
  • Hi Rena, I have updated my post above adding the code for the modal. Basically I don't have other JavaScript or jQuery code that is related to the modal or to the form. Let me know if you would like to see how I have changed the unobtrusive-ajax.js file following the steps from the link in my post. Thanks for looking into this. – Ivan Jun 07 '21 at 12:31
  • Hi @Ivan, I checked the file upload when using unobtrusive ajax, but it works well. The answer link you shared is in 2013, and now is 2021, I think unobtrusive ajax has been updated. Maybe could you please share your original not working code? Also, for the modal does not closed even click close button, it also works well, actually `data-dismiss="modal"` is used to close the modal. It is the bootstrap component. – Rena Jun 08 '21 at 07:44
  • Hi @Rena, I have edited my response above, adding the unobtrusive ajax code. Could you please let me know what version of it you are using so I could update if needed. In my controller I have public async Task SaveBrand(BrandModelView model, IFormFile file). If I don't use data-ext="true" in the form, file is null, when I add it - it's working, but then after submiting and returning PartialView - the modal is not being closed. However the modal is getting closed on other pages when there is no file uploading. – Ivan Jun 08 '21 at 19:32

0 Answers0