پرش به مطلب اصلی

Item Collector

کامپوننت x-item-collector برای جمع‌آوری و مدیریت آیتم‌ها در فرم‌ها استفاده می‌شود. این کامپوننت امکان افزودن، ویرایش و حذف آیتم‌ها را به صورت داینامیک فراهم می‌کند و کنترل کاملی روی ظاهر و رفتار آیتم‌ها دارد.


ویژگی‌ها

  • افزودن و مدیریت داینامیک آیتم‌ها
  • تعیین حداکثر تعداد آیتم‌ها (max)
  • امکان غیرفعال‌سازی ویرایش یا حذف آیتم‌ها (editable, clearable)
  • پشتیبانی از مقدار اولیه (values)
  • شخصی‌سازی کلاس کانتینر و آیتم‌ها (containerClass, itemsClass)
  • تعیین جهت نمایش آیتم‌ها (itemsOrientation)
  • امکان تعیین عنوان برای بخش آیتم‌ها (itemsTitle)
  • شخصی‌سازی متن دکمه افزودن (buttonText)

ورودی‌های کامپوننت

x-item-collector

نام پراپنوع دادهمقدار پیش‌فرضتوضیحات
namestringنام فیلد (اجباری)
idstringnullشناسه یکتا برای کامپوننت
containerClassstringnullکلاس CSS سفارشی برای کانتینر اصلی
gridClassstringnullکلاس CSS سفارشی برای چیدمان فیلدها
itemsClassstringnullکلاس CSS سفارشی برای بخش آیتم‌ها
itemsOrientationstring"horizontal"جهت نمایش عنوان آیتم‌ها (vertical یا horizontal)
itemsTitlestringnullعنوان نمایش داده شده برای بخش آیتم‌ها
itemsTitleSelectorstringnullسلکتور CSS برای انتخاب عنصر عنوان آیتم‌ها
itemsLayoutstring"grid"نوع چیدمان آیتم‌ها (grid یا flow)
disabledboolfalseغیرفعال‌سازی کامپوننت
maxintnullحداکثر تعداد آیتم‌های قابل افزودن
buttonTextstring"افزودن"متن دکمه افزودن آیتم
valuesarraynullآرایه مقادیر اولیه برای آیتم‌ها
clearablebooltrueامکان حذف آیتم‌ها
editablebooltrueامکان ویرایش آیتم‌ها
suppressLogicMessagesboolfalseغیرفعال‌سازی پیام‌های منطقی
useDeleteActionboolfalseفعال‌سازی اکشن سفارشی حذف آیتم‌ها از طریق callback

نکات مهم
  • برای تنظیم تعداد ستون‌ها در اندازه‌های مختلف صفحه، از کلاس‌های بوت استرپی row-cols-sm-* یا row-cols-md-* یا row-cols-lg-* یا غیره در itemsClass استفاده نمایید.

  • برای جلوگیری از درج مقادیر تکراری برای هر فیلد آیتم کالکتور، کافی است که اتریبیوت data-prevent-duplicates را به کامپوننت مورد نظر اضافه کنید

  • پراپرتی itemsLayout در حالت grid، آیتم‌ها را به صورت ساختارمند و منظم نمایش می‌دهد و در حالت flow، عناصر به صورت متوالی و با فاصله مناسب کنار هم قرار می‌گیرند.

نکته مهم درباره ولیدیشن

ولیدیشن در item-collector نیازمند توجه ویژه است:

  • اگر می‌خواهید مطمئن شوید که حتماً آیتمی در کامپوننت وارد شده باشد، باید از ولیدیشن required برای نام اصلی مانند item-collector-name استفاده کنید.

  • فیلدهای داخلی آیتم کالکتور (مثل x) قوانین خاص خود را دارند. هنگام درج آیتم، با توجه به مقادیر این فیلدها تعدادی اینپوت هیدن به صورت اندیسی به کامپوننت اضافه می‌شود و تا زمانی که آیتمی اضافه نشده باشد، این فیلدها اصلاً وجود ندارند. بنابراین ولیدیشن مستقیم روی آن‌ها (مانند item-collector-name.*.x) فقط زمانی اجرا می‌شود که آیتم اضافه شده باشد.

  • اگر می‌خواهید هنگام درج آیتم جدید، فیلدی اعتبارسنجی شود، باید ولیدیشن را روی همان فیلد اصلی (مثل x) قرار دهید، نه روی item-collector-name.*.x. با این حال، برای این فیلد یک نکته مهم وجود دارد: در صورتی که فیلد x را به صورت «required» تعریف کنید، حتی بعد از درج آیتم نیز فرم قابل ارسال نیست (زیرا فیلد x خالی می‌ماند). این موضوع از سمت زیرساخت مدیریت شده است؛ کافی است پراپ required را به فیلد مورد نظر بدهید تا به درستی کنترل شود.

  • سایر ولیدیشن‌ها (مانند قواعد max، numeric و...) را می‌توانید از طریق ruleها روی فیلدها تنظیم کنید.

  • اگر می‌خواهید مقدار تکراری درج نشود، کافی است برای آن فیلد data-prevent-duplicates را اضافه کنید.

مقداردهی با جاوااسکریپت

برای مقداردهی جاوااسکریپتی میتوانید به صورت زیر عمل کنید

const instance = $("#item-collector-sample").data("instance");
instance.setValues([
{
specialty: {
text: "رشته تخصصی",
value: "حساب",
},
credit_coefficient: {
text: "ضریب اعتبار",
value: "12",
},
created_at: {
text: "تاریخ ایجاد",
value: "1404/06/18",
},
province: {
text: "استان",
value: [
{
label: "تهران",
value: "1",
},
],
},
"cities[]": {
text: "شهر",
value: [
{
label: "ساری",
value: "1",
},
{
label: "شیراز",
value: "2",
},
],
},
},
]);

مثال‌ها

نمونه پایه

$data = [
[
'specialty' => [
'text' => 'رشته تخصصی',
'value' => 'حساب',
],
'credit_coefficient' => [
'text' => 'ضریب اعتبار',
'value' => '12',
],
'created_at' => [
'text' => 'تاریخ ایجاد',
'value' => '1404/06/18',
],
'province' => [
'text' => 'استان',
'value' => [
[
'label' => 'تهران',
'value' => '1',
],
],
],
'cities[]' => [
'text' => 'شهر',
'value' => [
[
'label' => 'ساری',
'value' => '1',
],
[
'label' => 'شیراز',
'value' => '2',
],
],
],
],
];
<x-item-collector name="item-collector" :values="$data">
<x-text-input name="specialty" label="رشته تخصصی" />
<x-number-input name="credit_coefficient" label="ضریب اعتبار" required show-separator />
<x-datetime-picker name="created_at" label="تاریخ ایجاد" required />
<x-select
name="province"
label="استان"
:items="[
['id' => '1', 'name' => 'تهران', 'value' => '1'],
['id' => '2', 'name' => 'اصفهان', 'value' => '2'],
['id' => '3', 'name' => 'گلستان', 'value' => '3'],
['id' => '4', 'name' => 'مازندران', 'value' => '4'],
['id' => '5', 'name' => 'فارس', 'value' => '5'],
['id' => '6', 'name' => 'خراسان رضوی', 'value' => '6'],
]"
/>
<x-multi-select
name="cities"
label="شهر"
:items="[
['id' => '1', 'name' => 'ساری', 'value' => '1'],
['id' => '2', 'name' => 'شیراز', 'value' => '2'],
['id' => '3', 'name' => 'بابل', 'value' => '3'],
['id' => '4', 'name' => 'مشهد', 'value' => '4'],
['id' => '5', 'name' => 'اصفهان', 'value' => '5'],
['id' => '6', 'name' => 'کرج', 'value' => '6'],
['id' => '7', 'name' => 'گرگان', 'value' => '7'],
['id' => '8', 'name' => 'تهران', 'value' => '8'],
]"
/>
</x-item-collector>
<x-item-collector name="item-collector-simple" items-title="قسط" :editable="false" :max="2">
<x-number-input name="payment-amount" label="مبلغ قسط" show-separator suffix="ریال" />
<x-datetime-picker name="payment-date" label="تاریخ قسط" />
</x-item-collector>
<x-item-collector
name="item-collector-row"
items-layout="flow"
grid-class="d-md-flex"
items-orientation="vertical"
items-title-selector="#item-collector-select"
>
<x-text-input containerClass="flex-fill" required name="field-1" label="برنامه‌ها" />
<x-select
id="item-collector-select"
containerClass="flex-fill"
required
name="field-2"
label="دستگاه مجری"
:items="[
['id' => '1', 'name' => 'تهران', 'value' => '1'],
['id' => '2', 'name' => 'اصفهان', 'value' => '2'],
['id' => '3', 'name' => 'گلستان', 'value' => '3'],
['id' => '4', 'name' => 'مازندران', 'value' => '4'],
['id' => '5', 'name' => 'فارس', 'value' => '5'],
['id' => '6', 'name' => 'خراسان رضوی', 'value' => '6'],
]"
/>
<x-text-input containerClass="flex-fill" required name="field-3" label="اولویت" />
</x-item-collector>

Item Collector Delete Action

معرفی

در ItemCollectorBaseController قابلیتی به نام delete action وجود دارد که اجازه می‌دهد قبل از حذف یک آیتم، یک منطق سفارشی اجرا شود.

با استفاده از این قابلیت می‌توان قبل از حذف:

  • پیام تایید نمایش داد.
  • شرایط حذف را بررسی کرد.
  • درخواست حذف به سرور ارسال کرد.
  • در صورت نیاز از حذف آیتم جلوگیری کرد.

در صورت فعال بودن این قابلیت، ابتدا callback مربوط به delete اجرا می‌شود و نتیجه آن مشخص می‌کند که آیتم حذف شود یا خیر.


نحوه استفاده

برای تعریف رفتار حذف باید از متد onAction استفاده شود:

instance.onAction("delete", async () => {
return true;
});

نکته مهم: Callback مربوط به delete باید حتماً یک Promise برگرداند تا کنترلر بتواند منتظر نتیجه عملیات بماند. بنابراین callback باید به صورت async تعریف شود یا یک Promise را return کند.


مثال تایید حذف

instance.onAction("delete", async () => {

const alert = await showConfirmationMessage({
type: "danger",
title: "تایید حذف",
text: "آیا از حذف این آیتم مطمئن هستید؟"
});

return alert.isConfirmed;

});
instance.onAction("delete", () => {

return new Promise(async (resolve) => {

try {
const response = await fetch("/api/delete-item", {
method: "DELETE"
});

resolve(response.ok);

} catch (error) {
resolve(false);
}

});

});

در این مثال:

  • اگر کاربر تایید کند → Promise مقدار true برمی‌گرداند و آیتم حذف می‌شود.
  • اگر کاربر لغو کند → Promise مقدار false برمی‌گرداند و آیتم حذف نمی‌شود.

استفاده از Event آیتم

در صورت نیاز می‌توان event کلیک حذف را نیز دریافت کرد:

instance.onAction("delete", async (e) => {

const item = $(e.currentTarget)
.closest(".x-item-collector-item");

const index = item.data("index");

console.log(index);

return true;

});

روند اجرا

  1. کاربر روی دکمه حذف کلیک می‌کند.
  2. callback مربوط به delete اجرا می‌شود.
  3. کنترلر منتظر Promise برگشتی می‌ماند.
  4. در صورت دریافت true آیتم حذف می‌شود.
  5. در صورت دریافت false حذف متوقف می‌شود.

نکات

  • Callback حذف حتماً باید Promise برگرداند.
  • مقدار نهایی Promise باید boolean باشد (true یا false).
  • اگر callback تعریف نشده باشد، حذف به صورت پیش‌فرض انجام می‌شود.