### CustomJs چیست ؟
Custom JS یک فایل سفارشی سازی مربوط به فرم های ساخته شده توسط ماژول Dynamic Form Builder است.
توسط این فایل میتوانید امکاناتی که در فرم ساز به صورت Visual ارائه نشده را به راحتی با کد های JavaScript به فرم خود اضافه کنید.
در ادامه با ساختار این فایل و همچنین با نحوه کدنویسی آن آشنا خواهیم شد.
**تذکر :**
پیش نیاز این مستند، یاد داشتن JavaScript میباشد، همچنین بهتر است که با کتابخانه Knockout.js آشنایی داشته باشید.
وقتی که برای اولین بار فایل Custom JS را در فرم میسازید، متوجه میشوید که در این فایل به طور پیش فرض تعدادی function وجود دارند.
این function ها همانند یک قلاب، کد های Custom JS شما را به هسته فرم ساز متصل میکنند، هرکدام از این function ها به یک بخشی از فرم ساز متصل هستند که در ادامه تک تک آنها بررسی خواهند شد.
توجه داشته باشید که نیازی به وجود همه این function ها در فایل Custom JS شما نیست. شاید شما فقط به یکی یا چند تا از این function ها نیاز داشته باشید. پس میتوانید function هایی که به کارتان نمی آید را پاک کنید تا از شلوغ شدن فایل Custom JS جلوگیری شود.
##### توابع فرم - onLoad:
```js
export const onLoad = function (viewModel) {
// ...
}
```
این function به محض لود شدن فرم اجرا خواهد شد.
برای مثال اگر شما بخواهید به محض لود شدن فرم یک Provider اجرا گردد و با توجه به خروجی Provider یک پیامی به کاربر نمایش داده شود. در این حالت میبایست از این function استفاده نمایید. (نحوه اجرا کردن Provider بعدا به طور مفصل آموزش داده خواهد شد)
**نکته :**
این فانکشن یک پارامتر به اسم viewModel میگیرد که بعدا به طور کامل بررسی خواهد شد.
---
##### توابع فرم - beforeSubmit :
```js
export const beforeSubmit = function (viewModel) {
// ...
return true/false
}
```
این تابع موقعی اجرا خواهد شد که کاربر روی دکمه ارسال فرم کلیک کرده اما هنوز داده های فرم به سرور ارسال نشده است.
**در صورتی که در این تابع مقدار false یا Promise را با مقدار false برگشت دهید، فرم ارسال نخواهد شد.**
بعنوان مثال، در کد زیر وقتی اقدام به ارسال فرم شود، در ابتدا از کاربر پرسیده میشود که آیا از ارسال فرم اطمینان دارید یا خیر، در صورتی که کاربر روی دکمه بلی کلیک کرد فرم ارسال میشود و در غیر این صورت ارسال نخواهد شد:
```js
export const beforeSubmit = function(viewModel) {
return new Promise(resolve => {
DevExpress.ui.dialog.confirm('هشدار', 'از ارسال فرم اطمینان دارید؟').done(isOK => {
if (isOK) {
resolve(true);// فرم ارسال شود
} else {
resolve(false);// فرم ارسال نشود
}
});
});
}
```
نتیجه:

----
##### توابع فرم - onSubmitSuccess :
```js
export const onSubmitSuccess = function (viewModel) {
console.log('Submiting done from custom js');
}
```
این تابع زمانی اجرا خواهد شد که فرم با موفقیت ارسال شده است به گونه ای که زمانی که عملیات اصلی فرم که ثبت داده در پایگاه داده است مورد استفاده قرار میگیرد. به طور مثال نیاز دارید بعد از submit فرم رخدادی دیگر اجرا شود (نمایش یک فرم دیگر، شروع فرایند خارجی و ...)
**در صورتی که در این تابع مقدار false یا Promise را با مقدار false برگشت دهید، صفحه بارگذاری مجدد نخواهد شد.**
بطور مثال، اگر بخواهید بعد از ارسال موفقیت آمیز فرم کاربر به صفحه داشبورد هدایت شود میتوانید از کد زیر استفاده کنید:
```js
export const onSubmitSuccess = function(viewModel) {
return new Promise(resolve => {
resolve(false);// جلوگیری از رفرش شدن صفحه
setTimeout(() => {
location.href = '/Dashboard';// هدایت کاربر به صفحه داشبورد بعد از 1 ثانیه
}, 1000);
});
}
```
نتیجه:

----
##### توابع فرم - onSubmitFailed :
```js
export const onSubmitFailed = function (viewModel, error) {
console.error('Submiting failed from custom js', error);
}
```
پارامتر ها :
**error :**
خطایی که رخ داده است.
این تابع زمانی اجرا می شود که فرایند ارسال فرم به هر دلیلی با خطا مواجه شود، با کمک این تابع میتوانید خطای پیش آمده را مدیریت کنید.
---
##### توابع فیلد تودرتو (ساب فرم) - beforeSubSubmit :
```js
export const beforeSubSubmit = function (name, record, viewModel) {
// ...
}
```
پارامتر ها :
**name :**
نام فیلد تودرتو (ساب فرم)
**record :**
رکوردی که کاربر قصد ارسال کردن آن را دارد.
این تابع زمانی اجرا خواهد شد که کاربر قصد ارسال کردن رکورد در سطح subform را داشته باشد.
**در صورتی که در این تابع مقدار false یا Promise را با مقدار false برگشت دهید، فرم ارسال نخواهد شد.**
----
##### توابع فیلد تودرتو (ساب فرم) - onSubSubmit :
```js
export const onSubSubmit = function (name, record, viewModel) {
// ...
}
```
**name :**
نام فیلد تودرتو (ساب فرم)
**record :**
رکوردی که کاربر قصد ارسال کردن آن را دارد.
این تابع زمانی اجرا خواهد شد که فرم با موفقیت ارسال شده است به گونه ای که زمانی که عملیات اصلی فرم که ثبت داده در پایگاه داده است مورد استفاده قرار میگیرد. به طور مثال نیاز دارید بعد از submit فرم رخدادی دیگر اجرا شود (نمایش یک فرم دیگر، شروع فرایند خارجی و ...)
##### توابع فیلد تودرتو (ساب فرم) - beforeSubDelete :
```js
export const beforeSubDelete = function (name, record, viewModel) {
// ...
}
```
این تابع زمانی اجرا خواهد شد که کاربر قصد حذف یک رکوردی از Subform را داشته باشد.
**در صورتی که در این تابع مقدار false یا Promise را با مقدار false برگشت دهید، رکورد حذف نخواهد شد.**
----
##### توابع فیلد تودرتو (ساب فرم) - onSubDelete :
```js
export const onSubDelete = function (name, record, viewModel) {
// ...
}
```
**name :**
نام فیلد تودرتو (ساب فرم)
**record :**
رکوردی که کاربر قصد ارسال کردن آن را دارد.
این تابع زمانی اجرا خواهد شد که کاربر رکوردی از تودرتو (ساب فرم) را با موفقیت حذف کند.
----
##### توابع فیلد تودرتو (ساب فرم) - onLoadSubForm :
```js
export const onLoadSubForm = function (name, record, viewModel) {
// ...
}
```
**name :**
نام فیلد تودرتو (ساب فرم)
**record :**
رکوردی که کاربر قصد ارسال کردن آن را دارد.
این تابع زمانی اجرا خواهد شد که کاربر قصد افزودن رکوردی را در قالب مدال دارد. (باز شدن مودال)
----
##### توابع فیلد تودرتو (ساب فرم) - onLoadEditSubForm :
```js
export const onLoadEditSubForm = function (name, record, viewModel) {
// ...
}
```
**name :**
نام فیلد تودرتو (ساب فرم)
**record :**
رکوردی که کاربر قصد ارسال کردن آن را دارد.
این تابع زمانی اجرا خواهد شد که کاربر قصد ویرایش رکوردی را در قالب مدال دارد. (باز شدن مودال)
##### توابع فیلد تب - onTabLoad :
```js
export const onTabLoad = function (tabPanelName, tabPanelItemName, viewModel) {
if (tabPanelName === 'MyTabPanel') {
if (tabPanelItemName === 'MyTabPanelItem') {
// ...
}
}
}
```
اینfunction به محض لود شدن یک Tab اجرا خواهد شد.
چرا باید همچین functiuonای داشته باشیم؟ در صورتی که در فرم خود از فیلد Tab استفاده کرده باشید تا زمانی که کاربر وارد زبانه تب مربوطه نشود فیلد های مربوط به آن زبانه لود نخواهند شد.
برای مثال شما یک فیلد Selective دارید و قصد دارید که یک رویداد onValueChanged به آن اضافه کنید، در صورتی که فیلد فیلد داخل Tab نباشد در این حالت باید کد های خود را در function onload بنویسید، اما اگر فیلد شما داخل یک تب باشد تا زمانی که کاربر وارد آن زبانه نشود فیلد شما اصلا ساخته نخواهد شد و در صورتی که در متد onload کدی برای آن فیلد نوشته باشید کار نخواهد کرد و با خطا مواجه خواهید شد.
**نکته بسیار بسیار مهمی که برای این تابع وجود دارد این است که این تابع برای هر زبانه هر تب، فقط یک بار اجرا خواهد شد.**
پارامتر ها :
**tabPanelName :**
این پارامتر نام مربوط به فیلد تب شما را به function پاس میدهد. برای مثال اگر در فرو خود چندین فیلد تب داشته باشید آنگاه با استفاده از این پارامتر میتوانید کد های مربوطه را تفکیک کنید.
**tabPanelItemName :**
این پارامتر نام زبانه تبی است که در فرم ساز مشخص کرده اید.
برای مثال، اگر شما یک فیلد تب داشته باشید و بخواهید به محض لود شدن تب آیتم دومی، روی فیلد داخلی آن focus شود میتوانید از کد زیر استفاده کنید:
```js
export const onTabLoad = function (tabPanelName, tabPanelItemName, viewModel) {
if (tabPanelName === 'TabPanel') {
if (tabPanelItemName === 'TabPanelItem2') {
viewModel.fields.NumberField_Instance.focus();
}
}
}
```
نتیجه:

---
##### توابع فیلد تب - onTabNext :
```js
export const onTabNext = function (tabPanelName, currentTabPanelItemName, targetTabPanelItemName, viewModel) {
if (tabPanelName === 'MyTabPanel') {
if (targetTabPanelItemName === 'MyTabPanelItem') {
// ...
}
}
}
```
این تابع زمانی اجرا خواهد شد که کاربر در فیلد تب به زبانه بعدی تب برود.
پارامتر ها :
**tabPanelName :**
این پارامتر نام مربوط به فیلد تب شما را به function پاس میدهد. برای مثال اگر در فرو خود چندین فیلد تب داشته باشید آنگاه با استفاده از این پارامتر میتوانید کد های مربوطه را تفکیک کنید.
**currentTabPanelItemName :**
این پارامتر نام زبانه تبی است که در فرم ساز مشخص شده و هم اکنون کاربر در آن زبانه است.
**targetTabPanelItemName :**
این پارامتر نام زبانه تبی است که در فرم ساز مشخص شده و کاربر قصد رفتن به آن تب را دارد.
**در صورتی که در این تابع، مقدار false یا Promise را با مقدار false برگشت دهید کاربر به تب بعدی نخواهد رفت.**
برای مثال، در صورتی که داخل تب آیتم خود یک checkBox داشته باشید و بخواهید کاربر قبل از رفتن به تب بعدی حتما آن checkBox را تایید کند میتوانید از کد زیر استفاده کنید:
```js
export const onTabNext = function (tabPanelName, currentTabPanelItemName, targetTabPanelItemName, viewModel) {
if (tabPanelName === 'TabPanel') {
if (currentTabPanelItemName === 'TabPanelItem1') {
if(viewModel.data().AcceptRules() !== true) {
toast.show('error', 'پذیرش قوانین سامانه اجباری است');
return false; // جلوگیری از رفتن به تب بعدی
}
}
}
}
```
نتیجه:

----
##### توابع فیلد تب - onTabPrevious:
```js
export const onTabPrevious = function (tabPanelName, currentTabPanelItemName, targetTabPanelItemName, viewModel) {
if (tabPanelName === 'MyTabPanel') {
if (targetTabPanelItemName === 'MyTabPanelItem') {
// ...
}
}
}
```
این تابع زمانی اجرا خواهد شد که کاربر در فیلد تب به زبانه قبلی تب برود.
پارامتر ها :
**tabPanelName :**
این پارامتر نام مربوط به فیلد تب شما را به function پاس میدهد. برای مثال اگر در فرو خود چندین فیلد تب داشته باشید آنگاه با استفاده از این پارامتر میتوانید کد های مربوطه را تفکیک کنید.
**currentTabPanelItemName :**
این پارامتر نام زبانه تبی است که در فرم ساز مشخص شده و هم اکنون کاربر در آن زبانه است.
**targetTabPanelItemName :**
این پارامتر نام زبانه تبی است که در فرم ساز مشخص شده و کاربر قصد رفتن به آن تب را دارد.
**در صورتی که در این تابع، مقدار false یا Promise را با مقدار false برگشت دهید کاربر به تب قبلی نخواهد رفت.**
---
##### تابع فیلد دکمه - onButtonFieldClick:
```js
export const onButtonFieldClick = function (buttonName, viewModel, record) {
if (buttonName === 'MyButtonName') {
return true;
}
}
```
این تابع زمانی اجرا خواهد شد که کاربر روی یک فیلد دکمه کلیک کند.
**نکته ای که در این تابع وجود دارد این است که در صورتی که فیلد دکمه عملیات زیرساختی داشته باشد (مانند باز کردن فرم یا ...) ابتدا این function اجرا خواهد شد و در صورتی که این تابع مقدار false یا Promise با مقدار false برگشت نداد، بعد از آن عملیات زیرساختی اجرا خواهد شد.**
برای مثال، اگر شما فرمی داشته باشید که کاربر بواسطه آن قادر خواهد بود کاربر جدید در سامانه بسازد و وظیفه ارسال داده های فرم به پرووایدر جهت ساخت کاربر بر عهده فیلد دکمه باشد و در صورتی که بخواهید قبل از اجرای پرووایدر ساخت کاربر، بررسی کنید که آیا نام کاربر از قبل وجود دارد یا خیر میتوانید از دستور زیر استفاده کنید:
```js
export const onButtonFieldClick = function (buttonName, viewModel, record) {
if (buttonName === 'Insert') {
return new Promise(resolve => {
absoluteMethodHelper.runProvider(101014, [
{name: 'username', value: viewModel.data().UserName()}
])
.then(result => {
if(result.HasError) {
toast.showResult(result);
resolve(false);// جلوگیری از اجرا شدن عملیات پس زمینه دکمه
return;
}
if(JSON.parse(result.Content.providerResult)[0].IsExists) {
toast.show('error', 'نام کاربری قبلا وجود دارد');
resolve(false);// جلوگیری از اجرا شدن عملیات پس زمینه دکمه
return;
}
resolve(true);//اجرای عملیات دکمه
})
});
}
}
```
نتیجه:

پارامتر ها :
**buttonName :**
این پارامتر نام فیلد دکمه ای است که کاربر روی آن کلیک کرده.
**record :**
فرض کنید که فیلد دکمه شما در تودرتو (ساب فرم) باشد، ممکن این تودرتو (ساب فرم) شما چندین رکورد داشته باشد. توسط این پارامتر میتوانید به رکورد مربوطه دسترسی داشته باشید.