اگه تا حالا با توسعه فرانت اند تو Odoo کار کرده باشی، حتما اسم publicWidget به گوشِت خورده. publicWidget در واقع همون روشی بود (و هنوز هم هست) که اودو برای کنترل رفتار صفحات سایتش استفاده میکنه.
یعنی مثلا وقتی میخوای تو فرم ثبت نام کاربر، یه اعتبارسنجی برای شماره موبایل بنویسی، یا یه مودال خاص باز کنی، یا حتی رو دکمه ها یه رفتار سفارشی بندازی، دقیقا publicWidget اون ابزار جادویی بود که باید سراغش میرفتی.
الان با اومدن OWL و سیستم جدیدش، خیلی ها فکر میکنن دیگه publicWidget منقرض شده، ولی هنوزم بخش بزرگی از ماژول های وب و سایت اودو روی این سیستم قدیمی اما قدرتمند کار میکنن.
پس اگه میخوای واقعا فرانت اند اودو رو بفهمی، باید publicWidget رو مثل کف دستت بشناسی.
publicWidget دقیقا چیه؟
publicWidget یه کلاس جاوا اسکریپتیه که از ساختار قدیمی اودو به اسم Widget میاد.
اودو با این سیستم یه راه ساده برای ساخت رفتارهای قابل تکرار و ماژولار تو بخش فرانتاند ایجاد کرده بود.
هر publicWidget در واقع یه کلاس جاوا اسکریپتیه که به یه المنت خاص در صفحه (بر اساس selector) وصل میشه و رویدادها (event ها) و رفتارهاش رو کنترل میکنه.
به زبان سادهتر:
اگه بخوای بگی "وقتی کاربر روی فیلد شماره موبایل کلیک کرد یا blur شد، یه اتفاق خاص بیفته"، publicWidget دقیقا این کار رو برات انجام میده.
ساختار اصلی publicWidget
هر publicWidget به این شکل تعریف میشه:
/** @odoo-module **/ import publicWidget from "@web/legacy/js/public/public_widget"; publicWidget.registry.MyWidgetName = publicWidget.Widget.extend({ selector: '.my-selector', events: { 'click .my-button': '_onClickButton', }, start: function () { console.log("Widget started!"); }, _onClickButton: function (ev) { alert("Button clicked!"); } });
بیا قدم به قدم بررسیش کنیم 👇
selector
اینجا مشخص میکنی ویجت روی کدوم بخش از صفحه سوار (mount) بشه. این selector به this.target مپ (map) میشه.
مثلاً اگه selector برابر .my-form باشه، این ویجت فقط روی عناصری که اون کلاس رو دارن اجرا میشه.
events
یه آبجکت از لیست رویدادهاست که کلیدش رویداد (مثل click, change, blur) و مقدارش اسم تابع هندلر هست. به این ساختار event mapping میگن که هر event روی selector به اصطلاح bind میشه
مثلاً click .my-button یعنی وقتی داخل المنت انتخابشده، یه عنصر با کلاس .my-button کلیک شد، تابع _onClickButton اجرا بشه.
start()
یه lifecycle hook که وقتی ویجت راهاندازی میشه اجرا میشه.
اینجا معمولاً مقداردهی اولیه، انتخاب المنت ها یا تنظیم لیسنرها انجام میشه.
ویجت ها هوک های دیگه ای هم دارن:
willStart(): قبل از start() اجرا میشه و می تونه async باشه و برای درخواست داده از سرور قبل از render استفاده میشه.
destroy(): قبل از حذف از DOM اجرا میشه و برای آزادسازی منابع یا listenerها قابل استفادست.
متد خصوصی _onClickButton
_onClickButtonیه event handler که event رو هندل می کنه. یعنی وقتی event رخ میده این اجرا میشه.
مثال واقعی — اعتبارسنجی شماره موبایل با پلاگین intlTelInput
توی این مثال فرض شده پلاگین رو به پروژه اضافه کردیم. حالا فرض کن یه فرم داری برای گرفتن شماره موبایل کاربر، و میخوای مطمئن بشی شماره وارد شده معتبره.
کد زیر دقیقا همون کاریه که publicWidget به راحتی انجام میده:
/** @odoo-module */ import publicWidget from "@web/legacy/js/public/public_widget"; publicWidget.registry.PhoneInputValidation = publicWidget.Widget.extend({ selector: '#mobile', events: { 'blur': '_onBlur', 'change': '_reset', 'keyup': '_reset' }, start: function () { this.input = this.el; this.form = this.input.closest('form'); this._onFormSubmit = this._onFormSubmit.bind(this); this.iti = window.intlTelInput(this.input, { initialCountry: "us", separateDialCode: true, nationalMode: false, autoPlaceholder: 'aggressive', formatOnDisplay: true, }); if (this.form) { this.form.addEventListener('submit', this._onFormSubmit); } }, _onBlur: function () { this._reset(); if (this.input.value.trim()) { if (this.iti.isValidNumber()) { this.input.classList.add("is-valid"); } else { this.input.classList.add("is-invalid"); } } }, _reset: function () { this.input.classList.remove("is-invalid", "is-valid"); }, _onFormSubmit: function (ev) { ev.preventDefault(); const mobile_input = document.querySelector('input[name="formated_mobile"]'); this._reset(); if (!this.iti.isValidNumber()) { this.input.classList.add("is-invalid"); return; } mobile_input.value = this.iti.getNumber(); this.form.submit(); } });
اینجا اتفاقی که میفته اینه که:
-
وقتی کاربر روی فیلد موبایل کلیک کنه یا
blur کنه، شماره بررسی میشه. -
اگه شماره معتبر نباشه، کلاس
is-invalid بهinput اضافه میشه. -
وقتی فرم ارسال میشه، شماره نهایی فرمت شده تو فیلد
formated_mobile ذخیره میشه.
خیلی تمیز، جدا از بقیه کدها و قابل استفاده مجدد.
چرا هنوز هم publicWidget مهمه؟
با اینکه OWL وارد دنیای جدید اودو شده، اما publicWidget هنوز در بخشهایی از Website، eCommerce، Portal و Blog به شدت استفاده میشه.
مزیت بزرگش اینه که بدون نیاز به تغییر ساختار قالب (QWeb) میتونی فقط با یه فایل جاوا اسکریپت رفتار صفحه رو کنترل کنی.
مزایا:
- ساده و سریع برای پروژههای کوچک
-
نیاز نداشتن به سیستم
Component -
قابل اجرا بدون تغییر ساختار
DOM -
پشتیبانی از تمام قالبهای
QWeb قدیمی
معایب:
- ساختار قدیمی و کمتر ماژولار
- دشوارتر برای تست و دیباگ
-
وابسته به
DOM ثابت - ناسازگار با ساختار
OWL وreactive state
نکاتی که در استفاده از publicWidget باید رعایت کنی
-
همیشه ویجت رو در مسیر
static/src/js/ بذار تا اودو به صورت خودکار لودش کنه. -
از دستور
/** @odoo-module */ در ابتدای فایل استفاده کن تا سیستم ماژول اودو بفهمه این فایل مدرن هست. -
اگه چند ویجت داری، نامگذاری
Registry ها رو دقیق انجام بده تا تداخلی پیش نیاد. -
اگه رفتار وابسته به
template خاصی داری،selector رو محدود به اون فرم کن تا در کل سایت اجرا نشه.
تفاوت publicWidget با OWL
در OWL، همه چیز بر اساس Component طراحی شده، یعنی به جای extend کردن Widget، شما یه Component میسازید و اون رو یا با mount() به یه بخش از DOM وصل میکنید، یا با <t t-component="MyComponent"/> مستقیماً داخل قالب QWeb میذارید.
اما در publicWidget شما فقط رفتار رو به المنت موجود در صفحه اضافه میکنید، بدون اینکه کامپوننت جدیدی بسازید.
بنابراین publicWidget بیشتر برای اسکریپتهای رفتاری و سبک مناسبه، در حالی که OWL برای اپلیکیشنهای پویا و پیچیده طراحی شده.
جمعبندی
publicWidget هنوز یکی از پایههای اصلی توسعه فرانتاند در Odoo محسوب میشه.
حتی اگر هدف نهاییت کار با OWL باشه، درک publicWidget باعث میشه درک بهتری از چرخه حیات، DOM و تعاملات کاربری در اودو پیدا کنی.
برای پروژههایی مثل:
- فرمهای ساده
- کنترل رویدادها
- اعتبارسنجیهای درجا
- نمایش پیامها یا مودالهای سبک
هنوز publicWidget یه گزینه عالی و سریع به حساب میاد.
در آینده یه مقاله درباره پیاده سازی همین ویجت با OWL مینویسم و در اختیارتون میذارم.