0

Hello I can get every data but I can't get the ProductIds, it gives me null in the Handle functions request parameter. SelectedIds is not null, I can get it without problem in my action. I hope someone can help.

EDIT:

After some debugging I see that my action gets triggered twice because of the cshtml. As you can see in

 <form class="form card-pretify" id="remaxForm" action="@Url.Action("EmployeeSessionPost","Product")" method="post" enctype="multipart/form-data">

I use the action as EmployeeSessionPost. Because of this it makes my action trigger twice, It was normally EmployeeSession before. But I see that I can't get the InstructorId and CsvFile from the controller.

With using EmployeeSessionPost as action parameter in this line, it triggers twice, in first trigger it gives me the selectedIds but not the CsvFile and InstructorId. In second trigger it gives me the CsvFile and InstructorId but not the selectedIds.

If I change the Url.Action parameter from EmployeeSessionPost to EmployeeSession, and it gives me the same result of first trigger.

EDIT2:

I removed the action of the form and changed the button type to 'button' here is the changes that I made from the comments. It prevents the double trigger but I can't get the CsvFile and InstructorId, I just get the selectedIds like this. Like I mentioned before

<!--begin::Container-->
<div class="container-fluid">
    <form class="form card-pretify" id="remaxForm" method="post" enctype="multipart/form-data">


<!--end::Licence-->
                <div class="card-footer">
                    <button type="button" class="btn btn-primary ml-3" id="btnUpdate">Update</button>
                </div>

I still have the same script block

[Authorize]
[HttpPost]
public async Task<IActionResult> EmployeeSessionPost(List<int> selectedIds)
{
    var csvFile = Request.Form.Files["CsvFile"];
    var instructorId = Convert.ToInt32(Request.Form["InstructorId"]);

    if (csvFile != null && csvFile.Length > 0)
    {
        using var stream = new MemoryStream();
        await csvFile.CopyToAsync(stream);
        await MediatrSend(new Services.Product.Queries.SetEmployeeSessionQuery
        {
            ProductIds = selectedIds,
            InstructorId = instructorId,
            CsvFile = csvFile,
        });
    }

    ModelState.AddModelError("CsvFile", "Please select a valid CSV file.");
    //return RedirectToAction("ParticipantList", "Product", new { culture = RouteData.Values["culture"].ToString(), selectedIds = selectedIds });

    return RedirectToAction("EmployeeSession", "Product", new { culture = RouteData.Values["culture"].ToString() });
}

My query file

   public class SetEmployeeSessionQuery : IRequest<int>
    {
        public List<int> ProductIds { get; set; }
        public int ProductId { get; set; }
        public int ProductEmployeeId { get; set; }
        public int InstructorId { get; set; }
        public ClaimModel ClaimModel { get; set; }
        public IFormFile CsvFile { get; set; }

    }

    public class SetEmployeeSessionQueryHandler : IRequestHandler<SetEmployeeSessionQuery, int>
    {
        private readonly IProductEmployeeSessionRepository _productEmployeeSessionRepository;
        private readonly IProductEmployeeRepository _productEmployeeRepository;

        public SetEmployeeSessionQueryHandler(IProductEmployeeSessionRepository productEmployeeSessionRepository, IProductEmployeeRepository productEmployeeRepository)
        {
            _productEmployeeSessionRepository = productEmployeeSessionRepository;
            _productEmployeeRepository = productEmployeeRepository;
        }

        public async Task<int> Handle(SetEmployeeSessionQuery request, CancellationToken cancellationToken)
        {
          .
          ..
          ...
          .... goes on

here is my cshtml

@model Remax.Services.DTOs.EmployeeSessionDTO;
@{
    ViewData["Title"] = "EmployeeSession";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="content d-flex flex-column flex-column-fluid" id="kt_content">
    <!--begin::Entry-->
    <div class="d-flex flex-column-fluid">
        <!--begin::Container-->
        <div class="container-fluid">
            <form class="form card-pretify" id="remaxForm" action="@Url.Action("EmployeeSession","Product")" method="post" enctype="multipart/form-data">
                <!--begin::Licence-->
                <div class="card card-custom" data-card="true" data-card-tooltips="false">
                    <div class="card-header">
                        <div class="card-header-inner" data-card-tool="toggle">
                            <div class="card-title">
                                <h3 class="card-label">@Html.Raw(Localizer["Form"])</h3>
                            </div>
                            <div class="card-toolbar">
                                <a href="#" class="btn btn-icon btn-sm btn-primary mr-1" data-toggle="tooltip" data-placement="top" title="@Localizer["OpenClose"]">
                                    <i class="ki ki-arrow-down icon-nm"></i>
                                </a>
                            </div>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="form-group row">
                            <div class="col-lg-4">
                                <label>@Html.Raw(Localizer["Instructor"]) *:</label>
                                <select class="form-control kt-selectpicker mr-1" data-actions-box="true" data-live-search="true" id="InstructorId" name="InstructorId">
                                    <option value="">@Html.Raw(Localizer["Select"])</option>
                                    @foreach (var o in Model.Instructors)
                                    {
                                        <option value="@(o.InstructorId)">@Html.Raw(o.NameSurname)</option>
                                    }
                                </select>
                            </div>
                            <div class="col-lg-4">
                                <label>@Html.Raw(Localizer["ZoomFile"]) *:</label>
                                <div class="custom-file">
                                    <input type="file" class="custom-file-input" id="CsvFile" name="CsvFile" accept=".csv">
                                    <label class="custom-file-label">@Html.Raw(Localizer["ZoomFile"])</label>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <!--begin::Card-->
                <div class="card card-custom">
                    <div class="card-header flex-wrap border-0 pt-6 pb-0">
                        <div class="card-title">
                            <h3 class="card-label">
                                @Html.Raw(Localizer["Products"])
                            </h3>
                        </div>
                        <div class="card-toolbar">
                            <!--begin::Button-->
                            <a href="javascript:;" class="btn btn btn-light-primary mr-2 f-filter-btn">
                                <i class="la la-filter"></i>@Html.Raw(Localizer["Filter"])
                            </a>
                            <!--end::Button-->
                        </div>
                    </div>
                    <div class="card-body">
                        <!--begin::Search Form-->
                        <div class="mb-7 f-filter-container" id="searchForm">
                            <div class="row align-items-center">
                                <div class="col-lg-12">
                                    <div class="row align-items-center mt-1">
                                        <div class="col-md-4 form-group">
                                            <label class="mr-3 mb-0 d-none d-md-block">@Html.Raw(Localizer["StartDate"]):</label>
                                            <div class="input-group-prepend">
                                                <select data-filter="1" class="form-control kt-selectpicker" data-actions-box="true" data-live-search="true" data-selected-text-format="count>1" id="Month" name="Month">
                                                    <option value="">@Html.Raw(Localizer["Select"])</option>
                                                    @for (byte i = 1; i < 13; i++)
                                                    {
                                                        <option value="@i">@(i.ToString().PadLeft(2, '0')) - @Html.Raw(Localizer[String.Format("Month{0}", i)])</option>
                                                    }
                                                </select>
                                                <input type="number" id="Year" name="Year" placeholder="@(Html.Raw(Localizer["Year"]))" class="form-control ml-1" data-filter="1" />
                                            </div>
                                        </div>
                                        <div class="col-md-4 form-group">
                                            <label class="mr-3 mb-0 d-none d-md-block">@Html.Raw(Localizer["Category"]):</label>
                                            <select data-filter="1" class="form-control kt-selectpicker" multiple="multiple" data-actions-box="true" data-live-search="true" data-selected-text-format="count>1" id="ProductCategory" name="ProductCategory">
                                                @for (byte i = 1; i <= 3; i++)
                                                {
                                                    <option selected value="@i">@Html.Raw(Localizer[String.Format("ProductType{0}", i)])</option>
                                                }
                                            </select>
                                        </div>
                                        <div class="col-md-4 form-group">
                                            <label class="mr-3 mb-0 d-none d-md-block">@Html.Raw(Localizer["ProductName"]):</label>
                                            <input type="text" class="form-control" id="ProductName" name="ProductName" placeholder="@Html.Raw(Localizer["ProductName"])" data-filter="1" />
                                        </div>
                                    </div>
                                    <div class="row justify-content-end mt-3">
                                        <div class="col-md-4 form-group text-right">
                                            <label class="mr-3 mb-0 d-none d-md-block">&nbsp;</label>
                                            <a href="javascript:;" class="btn btn-danger px-6 font-weight-bold" id="ktSearch">
                                                <i class="fa fa-search icon-nm"></i>
                                                <span>@Html.Raw(Localizer["Search"])</span>
                                            </a>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <!--end::Search Form-->
                        <!--begin: Datatable-->
                        <div class="datatable datatable-bordered datatable-head-custom" id="kt_datatable" data-url="@Url.Action("EmployeeSessionList","Product",new { culture=Model.Language})"></div>
                        <!--end: Datatable-->
                    </div>
                </div>
                <!--end::Card-->
                <!--end::Licence-->
                <div class="card-footer">
                    <button class="btn btn-primary ml-3" id="btnUpdate" name="btnUpdate">@Html.Raw(Localizer["Update"])</button>
                </div>
            </form>
        </div>
        <!--end::Container-->
    </div>
    <!--begin::Entry-->
</div>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

@section scripts{
    <script src="~/assets/js/pages/crud/ktdatatable/base/KtDatatable.js?v=@(AppSettings.Version)"></script>
    <script src="~/assets/js/pages/crud/ktdatatable/base/KtDatatable_tr.js?v=@(AppSettings.Version)"></script>

    <script src="~/assets/js/pages/crud/ktdatatable/base/KtDatatable_@(Model.Language.Substring(0,2)).js?v=@(AppSettings.Version)"></script>
    <script src="~/assets/js/pages/forms/product/productlist.js?v=@(AppSettings.Version)"></script>

    <script src="~/assets/js/pages/crud/ktdatatable/base/KtDatatable.min.js?v=@(AppSettings.Version)"></script>
    <script src="~/assets/js/pages/crud/ktdatatable/base/KtDatatable_@(Model.Language.Substring(0,2)).min.js?v=@(AppSettings.Version)"></script>

    <script>
        var remaxTableScroll = true;
        $.checkboxSingleClick = function () { };
        var selectedIds = [];

        var datatableCols = [
            {
                selector: true,
                field: 'Id',
                title: '@Html.Raw(Localizer["Code"])',
                width: 35,
            },
            {
                field: 'ProductName',
                title: '@Html.Raw(Localizer["Product"])',
                width: 235,
            },
            {
                field: 'CategoryName',
                title: '@Html.Raw(Localizer["Category"])',
                width: 150,
            },
            {
                field: 'StartDateStr',
                title: '@Html.Raw(Localizer["StartDate"])',
                sortable: 'desc',
                width: 90,
            },
        ];
        $(document).ready(function () {
            datatable.on('datatable-on-check',
                function (e, args) {
                    $.each(args, function (i, a) {
                        if (selectedIds.indexOf(a) == -1)
                            selectedIds.push(a);
                    })
                    if (selectedIds.length > 0)
                        $('#setMultiple').show();
                    console.log(selectedIds);
                });
            datatable.on('datatable-on-uncheck',
                function (e, args) {
                    $.each(args, function (i, a) {
                        selectedIds.pop(a);
                    })
                    if (selectedIds.length == 0)
                        $('#setMultiple').hide();
                    console.log(selectedIds);
                });
            $('#btnUpdate').on('click', function () {
                $.ajax({
                    type: "POST",
                    dataType: 'json',
                    cache: false,
                    url: '@Url.Action("EmployeeSessionPost", "Product")',
                    cache: false,
                    data: {
                        selectedIds: selectedIds
                    },
                    success: function (data) {
                    }, async: true,
                });
            });
        });

    </script>
}
  • 1
    You are submitting the form using jQuery *and* also using the form post action. That's why you are seeing two submits. Change your button to ` – Rosdi Kasim Apr 18 '23 at 14:20
  • 1
    Here is some reading about button type: https://html.com/attributes/button-type/ – Rosdi Kasim Apr 18 '23 at 14:24
  • I understand, it looks a good usage. But If I delete my jquery button function I can't recieve my selectedIds. It is just for recieving the selectedIds. If I delete this code block, I can easily retrieve the CsvFile and InstructorId data but not selectedIds. I hope you can also give me suggestion on that – Onur Serbes Apr 18 '23 at 14:30
  • Then remove the form action and change your button type to 'button'. So when you click 'Update' only jQuery will be submitting the form. – Rosdi Kasim Apr 18 '23 at 15:04
  • I still heve the same reponse after I did that, can you check the EDIT 2 – Onur Serbes Apr 18 '23 at 15:28
  • If this EDIT is not implemented with the right way, can you modify the cshtml code and post it as an answer please ? It can be better for me to understand my mistake. – Onur Serbes Apr 18 '23 at 15:36
  • Seems you have solved the double posting issue. For the csv / instructorId, that's due to your javascript. Simplify your js to the most basic level to reproduce the issue. That's how you learn programming. – Rosdi Kasim Apr 18 '23 at 15:55
  • Thanks for your comments, I fix the problem and shared the solution in answer section. You can check it from here – Onur Serbes Apr 19 '23 at 10:17

1 Answers1

1

Thanks for all the comments, here is my solution. Instead of posting seperate ( both form and ajax ) I wanted to collect all of the data in form and retrieve the data from same FormData via ajax. Here is the modified code

controller/action

[Authorize]
        [HttpPost]
        public async Task<IActionResult> EmployeeSessionPost()
        {

            var csvFile = Request.Form.Files.GetFile("CsvFile");
            var instructorId = int.Parse(Request.Form["InstructorId"]);
            var selectedIds = Request.Form["SelectedIds"].ToString().Split(',').Select(int.Parse).ToList();

            if (csvFile != null && csvFile.Length > 0)
            {
                using var stream = new MemoryStream();
                await csvFile.CopyToAsync(stream);
                await MediatrSend(new Services.Product.Queries.SetEmployeeSessionQuery
                {
                    ProductIds = selectedIds,
                    InstructorId = instructorId,
                    CsvFile = csvFile,
                });
            }

            ModelState.AddModelError("CsvFile", "Please select a valid CSV file.");
            //return RedirectToAction("ParticipantList", "Product", new { culture = RouteData.Values["culture"].ToString(), selectedIds = selectedIds });
            return RedirectToAction("EmployeeSession", "Product", new { culture = RouteData.Values["culture"].ToString()});
        }

cshtml

<form class="form card-pretify" id="remaxForm" action="@Url.Action("EmployeeSessionPost","Product")" method="post" enctype="multipart/form-data">


 <div class="card-footer">
                    <button type="button" class="btn btn-primary ml-3" id="btnUpdate" name="btnUpdate">@Html.Raw(Localizer["Update"])</button>
                </div>


<script>
        var remaxTableScroll = true;
        $.checkboxSingleClick = function () { };

        var selectedIds = [];
        var formdata = new FormData();

        $(document).ready(function () {
            datatable.on('datatable-on-check',
                function (e, args) {
                    $.each(args, function (i, a) {
                        if (selectedIds.indexOf(a) == -1)
                            selectedIds.push(a);
                    })
                    if (selectedIds.length > 0)
                        $('#setMultiple').show();
                    console.log(selectedIds);
                });
            datatable.on('datatable-on-uncheck',
                function (e, args) {
                    $.each(args, function (i, a) {
                        selectedIds.splice(selectedIds.indexOf(a), 1);
                    })
                    if (selectedIds.length == 0)
                        $('#setMultiple').hide();
                    console.log(selectedIds);
                });

            $('#btnUpdate').on('click', function () {

                var csvFile = $('#CsvFile').prop('files')[0];
                var instructorId = $('#InstructorId').val();

                formdata.append('CsvFile', csvFile);
                formdata.append('InstructorId', instructorId);
                formdata.append('SelectedIds', selectedIds)

                $.ajax({
                    type: "POST",
                    dataType: 'json',
                    cache: false,
                    url: '@Url.Action("EmployeeSessionPost", "Product")',
                    data: formdata,
                    processData: false,
                    contentType: false,
                    success: function (data) {
                        console.log(data);
                    },
                    error: function (xhr, status, error) {
                        console.log(xhr.responseText);
                    }
                });
            });
        });
    </script>
  • If you have better solution to fix it, or any advise to make this implementation better, please share with me in the comments – Onur Serbes Apr 19 '23 at 10:18