فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: هفت دقیقه

پیشنیاز این بحث مطالعه‌ی مطلب «صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC» است و در اینجا جهت کوتاه شدن بحث، صرفا به تغییرات مورد نیاز جهت اعمال بر روی مثال اول اکتفاء خواهد شد.


تغییرات مورد نیاز جهت فعال سازی ویرایش، حذف و افزودن رکوردهای jqGrid

می‌خواهیم در بدو نمایش گرید، یک ستون خاص دارای دکمه‌های ویرایش و حذف ظاهر شوند:


برای اینکار تنها کافی است در انتهای ستون‌های تعریف شده، یک ستون خاص را با formatter مساوی actions ایجاد کنیم:
                colModel: [
                    {
                        // سایر ستون‌ها
                        name: 'myac', width: 80, fixed: true, sortable: false,
                        resize: false, formatter: 'actions',
                        formatoptions: {
                            keys: true
                        }
                    }
                ],
برای اینکه دکمه‌های ویرایش و حذف ردیف‌های آن عمل کنند:


نیاز است تعاریف سایر ستون‌هایی را که باید قابلیت ویرایش داشته باشند، به نحو ذیل تغییر دهیم:
                colModel: [
                    {
                        name: 'Id', index: 'Id', align: 'right', width: 70,
                        editable: false
                    },
                    {
                        name: 'Name', index: 'Name', align: 'right', width: 100,
                        editable: true, edittype: 'text',
                        editoptions: {
                            maxlength: 40
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Supplier.Id', index: 'Supplier.Id', align: 'right', width: 110,
                        editable: true, edittype: 'select',
                        editoptions: {
                            dataUrl: '@Url.Action("SuppliersSelect","Home")'
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Category.Id', index: 'Category.Id', align: 'right', width: 110,
                        editable: true, edittype: 'select',
                        editoptions: {
                            dataUrl: '@Url.Action("CategoriesSelect","Home")'
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Price', index: 'Price', align: 'center', width: 100,
                        formatter: 'currency',
                        formatoptions:
                        {
                            decimalSeparator: '.',
                            thousandsSeparator: ',',
                            decimalPlaces: 2,
                            prefix: '$'
                        },
                        editable: true, edittype: 'text',
                        editrules: {
                            required: true,
                            number: true,
                            minValue: 0
                        }
                    },
                    {
                        name: 'myac', width: 80, fixed: true, sortable: false,
                        resize: false, formatter: 'actions',
                        formatoptions: {
                            keys: true
                        }
                    }
                ],
- در اینجا هر ستونی که دارای خاصیت editable مساوی true است، قابلیت ویرایش پیدا می‌کند.
- edittype آن بیانگر کنترلی است که باید حین ویرایش آن سلول خاص ظاهر شود. برای مثال اگر text باشد، یک text box و اگر مانند حالت Supplier.Id مساوی select تعریف شود، یک drop down را ظاهر خواهد کرد. برای مقدار دهی این drop down می‌توان editoptions و سپس dataUrl آن‌را مقدار دهی نمود.
        public ActionResult SuppliersSelect()
        {
            var list = ProductDataSource.LatestProducts;
            var suppliers = list.Select(x => new SelectListItem
            {
                Text = x.Supplier.CompanyName,
                Value = x.Supplier.Id.ToString(CultureInfo.InvariantCulture)
            }).ToList();
            return PartialView("_SelectPartial", suppliers);
        }
در مثال فوق، این dataUrl به اکشن متد SuppliersSelect اشاره می‌کند که نهایتا لیستی از تولید کننده‌ها را توسط partial view ذیل بازگشت می‌دهد:
 @model IList<SelectListItem>

@Html.DropDownList("srch", Model)
در کل مقادیر قابل تنظیم در اینجا شامل  text، textarea، select، checkbox، password، button، image، file و custom هستند.
- خاصیت editrules، برای مباحث اعتبارسنجی اطلاعات ورودی توسط کاربر پیش بینی شده‌است. برای مثال اگر required: true در آن تنظیم شود، کاربر مجبور به تکمیل این سلول خاص خواهد بود. در اینجا خواصی مانند number و integer از نوع bool، خاصیت‌های minValue و maxValue از نوع عددی، email, url, date, time از نوع bool و custom قابل تنظیم است (مثال‌های حالت custom را در منابع انتهای بحث می‌توانید مطالعه کنید).
- پس از اینکه مشخص شدند کدامیک از ستون‌ها باید قابلیت ویرایش داشته باشند، مسیری که باید اطلاعات نهایی را به سرور ارسال کند، توسط خاصیت editurl مشخص می‌شود:
$('#list').jqGrid({
   caption: "آزمایش چهارم",
   //url from wich data should be requested
   url: '@Url.Action("GetProducts","Home")',
   //url for edit operation
   editurl: '@Url.Action("EditProduct","Home")',
اکشن متد متناظر با این آدرس یک چنین شکلی را می‌تواند داشته باشد:
        [HttpPost]
        public ActionResult EditProduct(Product postData)
        {
            //todo: Edit product based on postData

            return Json(true);
        }
- تعاریف مسیرهای ارسال اطلاعات Add و Delete، در قسمت تنظیمات navGrid باید ذکر شوند:
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: false, search: false },
                //edit options
                {},
                //add options
                { width: 'auto', url: '@Url.Action("AddProduct","Home")' },
                //delete options
                { url: '@Url.Action("DeleteProduct","Home")' }
                );
امضای این اکشن متدها نیز بسیار شبیه به اکشن متد ویرایش است:
        [HttpPost]
        public ActionResult DeleteProduct(string id)
        {
            //todo: Delete product
            return Json(true);
        }

        [HttpPost]
        public ActionResult AddProduct(Product postData)
        {
            //todo: Add product to repository
            return Json(true);
        }
- حالت ویرایش و حذفی که تا اینجا بررسی شد (ستون actions)، جزو خواص توکار این گرید است. اگر بخواهیم آن‌ها را دستی فعال کنیم (جهت اطلاعات عمومی) می‌توان از فراخوانی متد ذیل نیز کمک گرفت:
        var lastSel;
        function inlineEdit() {
            $('input[name=rdEditApproach]').attr('disabled', true);
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: false, search: false },
                //edit options
                {},
                //add options
                { width: 'auto', url: '@Url.Action("AddProduct","Home")' },
                //delete options
                { url: '@Url.Action("DeleteProduct","Home")' }
                );
            //add onSelectRow event to support inline edit
            $('#list').setGridParam({
                onSelectRow: function (id) {
                    if (id && id != lastSel) {
                        //save changes in row
                        $('#list').saveRow(lastSel, false);
                        lastSel = id;
                    }
                    //trigger inline edit for row
                    $('#list').editRow(id, true);
                }
            });
        };
در اینجا ابتدا همان تنظیمات مسیرهای Add و Delete انجام شده‌است. سپس با فراخوانی دستی متد editRow در زمان کلیک بر روی یک ردیف، همان کاری را که ستون actions در جهت فعال سازی خودکار حالت ویرایش سلول‌ها انجام می‌دهد، می‌توان شبیه سازی کرد. متد saveRow نیز کار ارسال اطلاعات تغییر کرده را به سرور انجام می‌دهد.
- برای فعال سازی خودکار فرم‌های افزودن رکوردها و یا ویرایش ردیف‌های موجود می‌توان از فراخوانی متد formEdit ذیل کمک گرفت:
        function formEdit() {
            $('input[name=rdEditApproach]').attr('disabled', true);
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: true, search: false },
                //edit option
                {
                    width: 'auto', checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    }
                },
                //add options
                {
                    width: 'auto', url: '@Url.Action("AddProduct","Home")',
                    reloadAfterSubmit: false, checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    }
                },
                //delete options
                {
                    url: '@Url.Action("DeleteProduct","Home")', reloadAfterSubmit: false
                })
            .jqGrid('navButtonAdd', "#pager", {
                caption: "حذف ردیف‌های انتخابی", title: "Delete Toolbar", buttonicon: 'ui-icon ui-icon-trash',
                onClickButton: function () {
                    var idsList = jQuery("#list").jqGrid('getGridParam', 'selarrrow');
                    alert(idsList);
                    //jQuery("#list").jqGrid('delGridRow',idsList,{reloadAfterSubmit:false});
                }
            });
        };

        function centerDialog(form, grid) {
            var dlgDiv = $("#editmod" + grid[0].id);
            var parentDiv = dlgDiv.parent(); // div#gbox_list
            var dlgWidth = dlgDiv.width();
            var parentWidth = parentDiv.width();
            var dlgHeight = dlgDiv.height();
            var parentHeight = parentDiv.height();
            var parentTop = parentDiv.offset().top;
            var parentLeft = parentDiv.offset().left;
            dlgDiv[0].style.top =  Math.round(  parentTop  + (parentHeight-dlgHeight)/2  ) + "px";
            dlgDiv[0].style.left = Math.round(  parentLeft + (parentWidth-dlgWidth  )/2 )  + "px";
        }
ابتدای تنظیمات آن، شاهد add: true, del: true, edit: true هستید. این مورد سبب می‌شود تا در فوتر گرید، سه دکمه‌ی افزودن، ویرایش و حذف ردیف‌ها ظاهر شوند:


با کلیک بر روی دکمه‌ی افزودن ردیف جدید، صفحه‌ی ذیل به صورت خودکار تولید می‌شود:


و با کلیک بر روی دکمه‌ی ویرایش ردیفی انتخاب شده، صفحه‌ی ویرایش آن ردیف به همراه مقادیر سلول‌های آن ظاهر خواهند شد:


تنظیمات قسمت‌های Add و Delete ویرایش توسط فرم‌ها، با حالت ویرایش داخل ردیفی آنچنان تفاوتی ندارد. فقط در اینجا پیش از نمایش فرم، از متد centerDialog برای نمایش صفحات افزودن و ویرایش رکوردها در وسط صفحه، استفاده شده‌است. توسط checkOnUpdate: true, checkOnSubmit: true سبب خواهیم شد تا اگر کاربر مقادیر موجود فرمی را تغییر داده‌است و سعی در بستن فرم، بدون ذخیره سازی اطلاعات کند، پیغام هشدار دهنده‌ای به او نمایش داده شود که آیا می‌خواهید تغییرات را ذخیره کنید یا خیر؟


- در انتهای متد formEdit، به کمک متد jqGrid و پارامتر navButtonAdd یک دکمه‌ی سفارشی را نیز اضافه کرده‌ایم. اگر به ستون پس از شماره‌های خودکار ردیف‌ها، در سمت راست گرید دقت کنید، یک سری chekbox قابل مشاهده هستند. برای فعال سازی خودکار آن‌ها کافی است خاصیت multiselect گرید به true تنظیم شود. اکنون برای دسترسی به این ستون‌های انتخاب شده، می‌توان از متد jqGrid به همراه پارامترهای getGridParam و selarrrow استفاده کرد. خروجی آن، لیست idهای ستون‌ها است.


برای مطالعه بیشتر

Common Editing Properties
Inline Editing
Form Editing
Cell Editing


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid04.zip
  • #
    ‫۱۰ سال و ۳ ماه قبل، شنبه ۱۴ تیر ۱۳۹۳، ساعت ۱۷:۰۳
    با تشکر بسیار جهت ارائه این مطلب اما یک نکته اینکه با وجود  ارائه KENDO GRID  بصورت open source :
    آیا این گرید هم مثل kendo grid  در صفحه بندی هنگام استفاده بعنوان partial مشکل دارد؟
    • #
      ‫۱۰ سال و ۳ ماه قبل، شنبه ۱۴ تیر ۱۳۹۳، ساعت ۱۷:۱۹
      - کل Kendo UI سورس باز هست. اما مجوز عمومی استفاده از آن GPL است. یعنی باید کل کارتان را سورس باز کنید یا مجوز آن‌را بخرید.
      - اخیرا یک نسخه‌ی سبک‌تر از Kendo UI با مجوز BSD ارائه شده که Grid آن‌را ندارد (به عمد).
      بنابراین از این لحاظ، مجوز jqGrid بهتر است. مجوز عمومی آن MIT است و در هر نوع پروژه‌ای قابل استفاده‌است. مجوز تجاری هم دارد برای حالتیکه بخواهید کامپوننت‌های ASP.NET آن‌را بخرید که ... نیازی نیست (^ و ^).
      3. Can be used in proprietary works
      The license policy allow you to use this piece of code even inside commercial (not open source) 
      projects. So you can use this software without giving away your own (precious?) source code.

      سایر مسایل خارج از بحث جاری است.
  • #
    ‫۱۰ سال و ۲ ماه قبل، جمعه ۱۰ مرداد ۱۳۹۳، ساعت ۱۳:۵۲
    سلام.خسته نباشید.
    اگه بخواهیم توی ویرایش به صورت Dialog به جای DropDownList از JQuery AutoComplete استفاده کنیم باید چه تغییری بدیم؟
    مرسی
  • #
    ‫۱۰ سال و ۲ ماه قبل، دوشنبه ۲۰ مرداد ۱۳۹۳، ساعت ۱۸:۱۸
    در کد بالا، برای حالت ویرایش یک سلول در نوع select از mvc controller استفاده شده. من کد بالا رو برای webApi میخوام تنظیم کنم. رشته html رو تولید میکنم و پاس میدم به خروجی، اما خطا میده. 
    کد رو ببینید.
     [HttpGet]
    public string SelectAllJqTree()
            {
                var result = _kpiTypeService.SelectAll().Select(e => new
                {
                    value = e.Id,
                    text = e.Name,
                });
    
                var select = "<select>{0}</select>";
                var option = "<option value='{0}' >{1}</option>";
    
                var options = "";
    
                foreach (var item in result)
                {
                    options += string.Format(option, item.value, item.text);
                }
    
                return  string.Format(select, options).Trim();
    }
    و در کد js ستون "kpiType" رو هم اینجوری تنظیم کردم:
     colModel: [
                        {
                            name: 'KpiTypeID', index: 'KpiTypeID', align: 'right', width: 300,
                            editable: true, edittype: 'select',
                            editoptions: {
                                dataUrl: '/api/KPIType/SelectAllJqTree'
                            },
                            editrules: {
                                required: true
                            }
                        }
                  ]
    خروجی این رو با جی کوئری که با صفحه اضافه میکنم، یک کنترل صحیح رو تولید میکنه.
    $.ajax({
                    url: "/api/KPIType/SelectAllJqTree",
                    context: document.body
                }).done(function (data) {
                    $("#selection").html(data);
                });

    اما اینجا بهم پیام میده که 
    Uncaught Error: Syntax error, unrecognized expression: "<select><option value='1' >عملکردی</option><option value='2' >بودجه ای</option>
    چه نکته ای وجود داره؟ ایا مربوط به webApi هست؟
    • #
      ‫۱۰ سال و ۲ ماه قبل، دوشنبه ۲۰ مرداد ۱۳۹۳، ساعت ۱۸:۵۱
      - زمانیکه از یک اکشن متد، خروجی HTML دریافت می‌کنید، Content-Type آن مساوی text/html است. در حالت Web Api این مورد application/json یا حالات دیگر می‌تواند باشد (جهت دیباگ بهتر، برگه‌ی network فایرباگ را در این دو حالات با هم مقایسه کنید. بررسی کنید Response ارسالی چه محتوایی و چه Content-type ایی دارد).
      - ضمنا نیازی نیست اطلاعات select را در سمت سرور تولید کنید. امکان دریافت JSON از سرور و تبدیل آن به فرمت مورد نظر در سمت کلاینت هم پیش بینی شده‌است:
      editoptions: { dataUrl: '...url to get json....',
                     buildSelect: function (response) {
                          var data = typeof response === "string" ?  $.parseJSON(response.responseText) : response,
                          var s = "<select>";
                          s += '<option value="0">--No Manager--</option>';
                          $.each(data, function () {
                                s += '<option value="' + this.EmployeeId + '">' + this.EmployeeName + '</option>';
                          });
                      return s + "</select>";
                    }
      }
      در این حالت dataUrl شیء JSON مدنظر را از سرور دریافت می‌کند (آرایه‌ای از EmployeeId و EmployeeName ها). در رویدادگردان سمت کاربر buildSelect، این مورد دریافت و پردازش می‌شود.
      • #
        ‫۱۰ سال و ۲ ماه قبل، سه‌شنبه ۲۱ مرداد ۱۳۹۳، ساعت ۱۹:۴۵
        تشکر از پاسختون.
        با تنظیم نوع خروجی به json کار کرد
        [HttpGet]
        public IHttpActionResult SelectAllFake()
        {
                    var result = new List<KpiTypeView>
                    {
                        new KpiTypeView() {KpiTypeID = 1, KpiTypeName = "افزایشی"},
                        new KpiTypeView() {KpiTypeID = 2, KpiTypeName = "کاهشی"}
                    };
        
                    return Json(result);
        }

        • #
          ‫۱۰ سال و ۲ ماه قبل، سه‌شنبه ۲۱ مرداد ۱۳۹۳، ساعت ۲۰:۰۴
          البته در Web API اگر می‌خواهید همیشه خروجی JSON بگیرید می‌شود به نحو زیر هم عمل کرد:
          configuration.Formatters.Clear();
          configuration.Formatters.Add(new JsonMediaTypeFormatter());
  • #
    ‫۹ سال و ۱۰ ماه قبل، پنجشنبه ۶ آذر ۱۳۹۳، ساعت ۱۶:۴۱
    با سلام؛ من میخوام در هنگام Add, Update , Delete هنگام ارسال اطلاعات به سرور ValidateAntiForgeryToken رو هم بفرستم. چطور میشه اینکارو انجام بدم، روش‌های مختلفی رو جستجو و امتحان کردم ولی  جواب نداد.
    • #
      ‫۹ سال و ۱۰ ماه قبل، پنجشنبه ۶ آذر ۱۳۹۳، ساعت ۱۷:۰۸
      این روش عمومی است و با jqGrid هم امتحان شد، کار می‌کند:
              $(document).ready(function () {
                  var securityToken = $('[name=__RequestVerificationToken]').val();
                  $('body').bind('ajaxSend', function (elm, xhr, s) {
                      if (s.type == 'POST' && typeof securityToken != 'undefined') {
                          if (s.data.length > 0) {
                              s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
                          }
                          else {
                              s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
                          }
                      }
                  });
              });
      در این حالت فرقی نمی‌کند که از چه ابزاری برای ارسال اطلاعات به سرور استفاده می‌کنید. همینقدر که در پشت صحنه از jQuery Ajax استفاده می‌کند، رخداد ajaxSend آن هوک شده و پارامترهای لازم، به اطلاعات ارسالی به سرور اضافه می‌شوند.
      • #
        ‫۹ سال و ۱۰ ماه قبل، پنجشنبه ۶ آذر ۱۳۹۳، ساعت ۲۰:۲۹
        property خاصی از jqgrid باید مقدار دهی بشه؟ متاسفانه جواب نداد!
        • #
          ‫۹ سال و ۱۰ ماه قبل، پنجشنبه ۶ آذر ۱۳۹۳، ساعت ۲۱:۱۲
          ()Html.AntiForgeryToken@ را روی صفحه قرار دادید؟ بررسی name=__RequestVerificationToken در کد فوق، بر مبنای وجود این فیلد در صفحه‌است.

          • #
            ‫۹ سال و ۱۰ ماه قبل، شنبه ۸ آذر ۱۳۹۳، ساعت ۱۲:۰۹
            سلام
            بله ()Html.AntiForgeryToken@  در صفحه وجود دارد.
            • #
              ‫۹ سال و ۱۰ ماه قبل، شنبه ۸ آذر ۱۳۹۳، ساعت ۱۶:۱۰
              قبل از ارسال کدی که ملاحظه کردید، این موارد طی شدند:
              - به ابتدای View مثال 4 (مثال بحث جاری که کدهای کامل آن در پایان مطلب پیوست شده‌اند) ابتدا ()Html.AntiForgeryToken@ اضافه شد.
              - بعد در قسمت اسکریپت‌های صفحه، کد مرتبط با ajaxSend که توکن امنیتی را به انتهای اطلاعات درخواست اضافه می‌کند، اضافه شد.
              - در کدهای کنترلر، روی اکشن متدهای ثبت، ویرایش و حذف، ویژگی ValidateAntiForgeryToken اضافه شد.
              تصویر فوق هم بر همین مبنا تهیه شده‌است.
              برای آزمایش بیشتر، کدهای اسکریپتی ajaxSend حذف شدند. بعد سعی در ویرایش. نتیجه دریافت استثناء از طرف سرور بود. با برگرداندن کدهای اسکریپتی، مشکل صدور استثنای نبود توکن امنیتی برطرف شد.
  • #
    ‫۹ سال و ۹ ماه قبل، سه‌شنبه ۹ دی ۱۳۹۳، ساعت ۰۰:۵۲
    سلام
    زمانی که میخام حذف کنم بجای اینکه حذف براساس ایدی بانک باشه براساس ایدی ردیف گرید هست
    • #
      ‫۹ سال و ۹ ماه قبل، سه‌شنبه ۹ دی ۱۳۹۳، ساعت ۰۴:۵۲
      - مثال‌های سری jqGrid تغییرات زیادی داشتند. برای دریافت آن‌ها به این مخزن کد مراجعه کنید.
      - برای نمونه، این فایل بهبود یافته مثال جاری است. در آن نحوه‌ی تعریف ستون Id، به صورت مخفی و کلید، معرفی شده. همچنین در ستون actions آن نحوه‌ی معرفی آدرس حذف به نحو بهتری درج شده‌است. به علاوه نحوه‌ی استفاده از anti-forgery token در آن ذکر شده، به همراه StronglyTyped.PropertyName ها.
      • #
        ‫۹ سال و ۹ ماه قبل، چهارشنبه ۱۰ دی ۱۳۹۳، ساعت ۰۰:۴۸
        ممنون. ولی تو تعریف کدهای شما، ردیف رو به عنوان ایدی به تابع حذف می‌فرسته و با این تابع میشه نوع داده ارسالی رو مشخص کرد
           onClickButton: function (e) {
                    var id = $('#grid').jqGrid('getGridParam', 'selrow');
                   
                    if (id != null) {
                        var idHtml = $('#grid').jqGrid('getCell', id, 'ID_Baker');
                        //var idHtml = id.ID_Baker;
                        //var idHtml = $('#' + id).children().first().html();
                        $.ajax({
                            url: '@(Url.Action("DeleteProfile"))' + '?Id=' + idHtml + '&RowId=' + id,
                            success: function (data) {
                                if (data.Success) {
        
                                    $('#grid').jqGrid('delRowData', data.RowId);
                                }
                            }
                        });
                    }
                    else {
                    }
                },
        • #
          ‫۹ سال و ۹ ماه قبل، چهارشنبه ۱۰ دی ۱۳۹۳، ساعت ۰۱:۳۳
          مثال به روز شده را اجرا کردید؟ شماره Id اولین ردیف را به عمد اینبار از 100 تنظیم کردم (تا با شماره ردیف اشتباه نشود) و این حاصل کلیک بر روی دکمه‌ی حذف اولین ردیف است؛ بدون نیاز به کد اضافه‌تری (در این مثال، حذف ردیف شماره یک، عدد id مساوی 100 را به سرور ارسال کرده):

  • #
    ‫۸ سال قبل، یکشنبه ۲۸ شهریور ۱۳۹۵، ساعت ۱۳:۵۷
    سلام؛ آیا jqgrid این مکان رو داره که در InlineAdd بجای فرستادن اطلاعات به یک کنترلر ،به یک متغیر جاوااسکریپتی اطلاعات رو فرستاد؟ مثلا تو سناریو فاکتور و جزئیات فاکتور باید همه اطلاعات master-detail با هم به کنترلر مربوطه فرستاده بشه 
  • #
    ‫۳ سال و ۱۱ ماه قبل، پنجشنبه ۳ مهر ۱۳۹۹، ساعت ۱۶:۱۳
    سلام. من کد‌های زیر رو نوشتم اما دیلیت خطی ( ردیفی) برام کار نمی‌کنه و اررو 500 میده:
    $("#JQGrid1").jqGrid({
                    url: "/manager/Products/OnProductDataRequested",
                    editurl: '/manager/Products/EditProductData',
                    mtype: "GET",
                    datatype: "json",
                    page: 1,
                    sortname: 'Priority',
                    sortorder: "desc",
                    viewrecords: true,
                    jsonReader: { id: "Id" },
                    prmNames: { id: "Id" },
                    colNames: ["Id","خلاصه","توضیح خلاصه","قیمت","قیمت با تخفیف","درصد کارمزد سایت","آستانه هشدار موجودی","محتوا", "عنوان","فروشگاه","گروه","تعداد موجودی", "اولویت","محصولات مکمل","تصاویر","مقادیر مشخصه محصول","قیمت","نظرات", "فعال","ویژه","موجود","برند","عملیات درجا","عملیات کامل"],
                    colModel: [
                        { key: true, width: 50, name: "Id", hidden: true, search: false },
                        {
                            editable: true, width: 10, name: "Abstract", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "AbstractDescription", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "Value", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "Discount", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "SiteWagePercentage", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "InventoryAlertLimit", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 10, name: "Context", search: true, stype: "text", editable: true,
                            hidden: true,
                            editrules: { edithidden: true }
                        },
                        {
                            editable: true, width: 150, name: "Title", search: true, stype: "text",
                            searchoptions: { "sopt": ["bw", "eq"] }
                        },
                       {
                           name: "shopTitle", align: 'center', viewable: true, editrules: { edithidden: true },
                           search: true,
                           editable: true, stype: 'select',
                           edittype: 'select',
                           searchoptions: {
                               sopt: ["eq", "ne"],
                               dataUrl: "/manager/Products/Getshop/", buildSelect: function (data) {
                                   var response, s = '<select>', i;
                                   response = jQuery.parseJSON(data);
                                   if (response && response.length) {
                                       $.each(response, function (i) {
                                           s += '<option value="' + this.shId + '">' + this.shTitle + '</option>';
                                       });
                                   }
                                   return s + '</select>';
                               }, 
                           },
                           editoptions: {
                               dataUrl: "/manager/Products/Getshop",
                               buildSelect: function (data) {
                                   var response, s = '<select>', i;
                                   response = jQuery.parseJSON(data);
                                   if (response && response.length) {
                                       $.each(response, function (i) {
                                           s += '<option value="' + this.shId + '">' + this.shTitle + '</option>';
                                       });
                                   }
                                   return s + '</select>';
                               },
                           }
                       },
                        {
                            editable: true, name: "groupTitle", search: true, stype: "select"
                            , editrules: { edithidden: true },
                            search: true,
                            edittype: 'select',
                            editoptions: {
                            dataUrl:  "/manager/Products/GetGroups",
                              buildSelect: function (data) {
                                  var response, s = '<select>', i;
                                  response = jQuery.parseJSON(data);
                                  if (response && response.length) {
                                      $.each(response, function (i) {
                                          s += '<option value="' + this.grpId + '">' + this.grpTitle + '</option>';
                                      });
                                  }
                                  return s + '</select>';
                              },
                              }
                        },
                        {
                            editable: true, width: 70, name: "AvailableCount", search: true, stype: "number",
                            searchoptions: { "sopt": ["bw", "eq"] }
                        },
                       {
                           editable: true, width: 50, name: "Priority", search: true, stype: "number",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: false, width: 80, name: "ComplementProducts", search: true, stype: "text",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: false, width: 70, name: "Images", search: true, stype: "text",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: false, width: 100, name: "ProductProperty", search: true, stype: "text",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: false, width: 80, name: "Price", search: true, stype: "text",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: false, width: 80, name: "Comments", search: true, stype: "text",
                           searchoptions: { "sopt": ["bw", "eq"] }
                       },
                       {
                           editable: true, width: 50, name: "Active", search: true, formatter: 'checkbox', edittype: 'checkbox', editoptions: { value: "True:False" }
                           , formatoptions: { disabled: false}
                       },
                        {
                            editable: true, width: 80, name: "AmazingOffer", search: true, formatter: 'checkbox', edittype: 'checkbox', editoptions: { value: "True:False" }
                            , formatoptions: { disabled: false}
                        },
                        {
                            editable: true, width: 80, name: "Available", search: true, formatter: 'checkbox', edittype: 'checkbox', editoptions: { value: "True:False" },
                            searchoptions: { "sopt": ["bw", "eq"] }, formatoptions: { disabled: false }
                        },
                       
                           {
                               name: 'BrandId', align: 'center', hidden: true, viewable: true, editrules: { edithidden: true },
                               editable: true, stype: 'select',
                               edittype: 'select',
                               editoptions: {
                                   dataUrl: "/manager/Products/GetBrands",
                                   buildSelect: function (data) {
                                       var response, s = '<select>', i;
                                       response = jQuery.parseJSON(data);
                                       if (response && response.length) {
                                           $.each(response, function (i) {
                                               s += '<option value="' + this.brandId + '">' + this.brandTitle + '</option>';
                                           });
                                       }
                                       return s + '</select>';
                                   },
                               }
    
                           },
                        {
                            name: "myac", width: 80, fixed: true, sortable: false, resize: false, formatter: 'actions',
                            formatoptions: { keys: true }
                           },
                        {
                            editable: false, width: 70, name: "FullEdit", search: true, stype: "text",
                            searchoptions: { "sopt": ["bw", "eq"] }
                        },
                        // BLAH, BLAH, BLAH
                    ],
                    gridComplete: function () {
                        var ids = jQuery("#JQGrid1").jqGrid('getDataIDs');
                        for (var i = 0; i < ids.length; i++) {
                            var cl = ids[i];
                            ComplementProducts = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/ComplementProducts/Index/" + cl + " }) + '><span class=\"fa fa-shopping-cart\" style='color: white;'></span></a></div>";
                            Images = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/Images/Index/" + cl + " }) + '><span class=\"fa fa-picture-o\" style='color: white;'></span></a></div>";
                            ProductProperty = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/ProductPropertyItems/Index/" + cl + " }) + '><span class=\"fa fa-braille\" style='color: white;'></span></a></div>";
                            Price = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/pricesppitems/Index/" + cl + " }) + '><span class=\"fa fa-line-chart\" style='color: white;'></span></a></div>";
                            Comments = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/Products/Comments/" + cl + " }) + '><span class=\"fa fa-comments\" style='color: white;'></span></a></div>";
                            FullEdit = "<div class=\"btn-group\"><a class='btn btn-primary' href=/manager/Products/Edit/" + cl + " }) + '><span class=\"fa fa-edit\" style='color: white;'></span></a><a class='btn btn-primary' href=/manager/Products/Details/" + cl + " }) + '><span class=\"fa fa-exclamation-circle\" style='color: white;'></span></a></div>";
    
                            jQuery("#JQGrid1").jqGrid('setRowData', ids[i], { ComplementProducts: ComplementProducts, Images: Images, ProductProperty: ProductProperty, Price: Price, Comments: Comments, FullEdit: FullEdit});
                        }
                    }, loadComplete: function () {
    
                        var activeButton = getColumnIndexByName('Active');
                        var ids = jQuery("#JQGrid1").jqGrid('getDataIDs'); //id's
                        $("tbody > tr.jqgrow > td:nth-child(" + (activeButton + 1) + ") > input",
                          this).click(function (e) {
                              var rowId = $(e.target).closest("tr").attr("id");
                              // alert("active clicked " + rowId);
                              $.ajax({
                                  type: "POST",
                                  url: "/manager/Products/Activate",
                                  data: {
                                      id: rowId
                                  },
                                  dataType: "json"
                              });
    
                          });
    
    
    
                        var amazingOfferButton = getColumnIndexByName('AmazingOffer');
                        var ids = jQuery("#JQGrid1").jqGrid('getDataIDs'); //id's
                        $("tbody > tr.jqgrow > td:nth-child(" + (amazingOfferButton + 1) + ") > input",
                          this).click(function (e) {
                              var rowId = $(e.target).closest("tr").attr("id");
                              $.ajax({
                                  type: "POST",
                                  url: "/manager/Products/ShowInAmazingOffer",
                                  data: {
                                      id: rowId
                                  },
                                  dataType: "json"
                              });
                              $('#JQGrid1').trigger('reloadGrid');
                          });
    
                        var availableButton = getColumnIndexByName('Available');
                        var ids = jQuery("#JQGrid1").jqGrid('getDataIDs'); //id's
                        $("tbody > tr.jqgrow > td:nth-child(" + (availableButton + 1) + ") > input",
                          this).click(function (e) {
                              var rowId = $(e.target).closest("tr").attr("id");
                              $.ajax({
                                  type: "POST",
                                  url: "/manager/Products/Available",
                                  data: {
                                      id: rowId
                                  },
                                  dataType: "json"
                              });
                              $('#JQGrid1').trigger('reloadGrid');
                          });
                    },
                    height: "auto",
                    caption: "",
                    viewrecords: true,
                    rowNum: 20,
                    direction: "rtl",
                    pager: jQuery('#JQGrid1_pager'),
                    rowList: [10, 20, 30, 40],
                    toppager: true,
                    jsonReader:
                    {
                        root: "rows",
                        page: "page",
                        total: "total",
                        records: "records",
                        repeatitems: false,
                        Id: "0"
                    },
    
                }).jqGrid('navGrid', '#JQGrid1_pager',
                    // the buttons to appear on the toolbar of the grid
                    { edit: true, add: true, del: true, search: true, refresh: true, view: false, position: "left", cloneToTop: true,searchtext:"جستجو" },
                       // options for the Edit Dialog
                       {
                           width: 450,
                           editCaption: "ویرایش محصول",
                           recreateForm: true,
                           closeAfterEdit: true,
                           viewPagerButtons: false,
                           //afterShowForm: populateGroups,
                           errorTextFormat: function (data) {
                               return 'Error: ' + data.responseText
                           }
                    },
                       // Add options
                       { url: '/TabMaster/Create', closeAfterAdd: true },
                       // Delete options
                       { url: '/manager/Products/Remove' },
                       {
                           zIndex: 100,
                           caption: "جستجوی محصول",
                           sopt: ['cn']
                       }
                       );

     
    • #
      ‫۳ سال و ۱۱ ماه قبل، پنجشنبه ۳ مهر ۱۳۹۹، ساعت ۱۷:۴۹
      خطای 500 یعنی internal server error. بنابراین در کدهای سمت سرور شما مشکلی رخ داده که باید آن‌را لاگ و بررسی کنید. همچنین ابزار developer tools مرورگر در برگه‌ی network آن با انتخاب response خطا دار، ممکن است در برگه‌ی نمایش محتوای response، حاوی اصل خطای بازگشتی از سرور هم باشد.
      • #
        ‫۳ سال و ۱۱ ماه قبل، پنجشنبه ۳ مهر ۱۳۹۹، ساعت ۱۹:۲۷
        Failed to load resource: the server responded with a status of 500 (Internal Server Error)
        جالب اینجاست ویرایش خطی کار میکنه و وارد متد مربوطه میشه اما حذف خطی وارد متد مربوطه نمیشه.
        من نوعش از هم از حالت Get به Post تغییر دادم بازم خطا میداد.
         [HttpPost]
                public ActionResult EditProductData(string oper, string title,string groupTitle,string shopTitle,int AvailableCount,int Priority, int? id)
                {
                    if (oper == "add")
                    {
                        var product = new Product()
                        {
                            Title = title,
                            
                        };
                        db.Products.Add(product);
                        db.SaveChanges();
                        return Content("true");
                    }
                    else if (oper == "del")
                    {
                        var product = db.Products.Find(id);
                        db.Products.Remove(product);
                        return Content("true");
                    }
                    else if (oper == "edit")
                    {
                        var product = db.Products.Find(id);
                        product.Title = title;
                        product.GroupId = int.Parse(groupTitle);
                        product.ShopId = int.Parse(shopTitle);
                        product.AvailableCount = AvailableCount;
                        product.Priority = Priority;
                        db.SaveChanges();
                        return Content("true");
                    }
                    return Content("false");
                }