افزودن Web Component ها به اپلیکیشن انگولار

مقدمهچند روز پیش یک دیزاینی دستم رسید که باید پیاده اش میکردم. قرار بود این طراحی هم در Angular پیاده بشه هم در ReactJS. یک قسمت از دیزاین یک slide button داشت:با صحبتی که با همکارم کردم، در نهایت تصمیم گرفتیم این slide button با Web Component پیاده بشه. چرا؟ چون هم در Angular میشه اضافه ش کرد و هم در ReactJS یا در هر فریم ورک جاوااسکریپت یا وب اپلکیشن دیگه ای که در شرکت توسعه داده شده. من هم مدت ها بود با Web Component ها کار نکرده بودم و این موضوع بهانه ای شد که دانشم رو در مورد Web Component ها گردگیری کنم، slide button رو پیاده سازی کنم و این مقاله رو باهاتون به اشتراک بگذارم.همه مطالعه و تجربه اخیرم رو در سه بخش مختلف گردآوری کردم: آشنایی با Web Component ها، چی هستند و چه استفاده ای دارند؟چطوری به کمک Web Component این slide button رو بسازیم؟چطوری این Web Component رو به انگولار اضافه کنیم؟بخش اول: Web Component چی هست و چه استفاده ای داره؟در واقع Web Component یک مجموعه از فن آوری های تحت وب هست که با همدیگه جمع شدند تا برای ما امکان ساخت HTML Element هایی رو با دو ویژگی به ارمغان بیارند:۱. ویژگی اول شون اینه که reusable هستند. یعنی یک بار میسازیم شون و هر جا خواستیم ازشون استفاده میکنیم. ۲. ویژگی دوم encapsulation توانایی ها و رفتارشون هست. یعنی همه کارشون رو داخل خودشون انجام میدن و به بقیه کد هیچ کاری ندارند و بدون دخالت، در کنار سایر بخش های داکیومنت کار میکنند.وب کامپوننت ها با فناوری های جاوااسکریپت توسعه داده میشن و در همه فریم ورک های زبان جاوااسکریپت قابل استفاده هستند. یعنی اگر یک وب کامپوننت بسازیم، از Vanilla Javascript گرفته تا React و Angular و VueJS و ... همه جا میشه ازشون استفاده کرد.به صورت کلی reusable بودن کد همیشه یک مزیت به حساب میاد. هر چی کد و ساختار پیچیده تر باشه، استفاده از ساختارهای reusable اهمیت بیشتری پیدا میکنه. در مورد ساخت markup های پیچیده در HTML که با CSS و Javascript همراه هستند، همین وضعیت صدق میکنه و ابزار دست مون برای کاهش ریسک این پیچیدگی ها وب کامپوننت ها هستند.سه فناوری اصلی به ما امکان پیاده سازی وب کامپوننت ها رو می دهند: فناوری Costum elementsفناوری Shadow DOMفناوری HTML Templateفناوری Custom elementsمجموعه ای از رابط های جاوااسکریپت (APIs) هستند که امکان ساخت custom element و توسعه رفتارهای یک element را میدهند.فناوری Shadow DOMاول بریم سراغ DOM tree و مرورش کنیم. امیدوارم با ساختار درختی (tree) آشنایی داشته باشی. درخت ها یک root یا ریشه دارند که زیر مجموعه هیچ عضو دیگه ای نیست. دست روی هر عضوی از درخت بگذاریم، زیرمجموعه ریشه یا یک عضو دیگه هست:در HTML، المنت ها ساختاری دارند که میشه از زاویه ساختار درخت (tree) هم بهش نگاه کرد. به این معنی که یا داخل داکیومنت ( document ) هستند، و یا داخل المنت های دیگه هستند. هر المنتی رو که دست بزاریم روش، زیرمجموعه یک المنت دیگه است. بنابر این به این ساختار المنت ها میگیم DOM Tree.در نظر بگیرید که یک درخت ممکنه بجز ریشه عضو دیگه ای نداشته باشه (فقط خودش باشه). میشه اینطوری گفت که یک المنت در HTML همیشه یک DOM Tree داره که ریشه اون درخت، خودشه.اگر میخواید بیشتر با DOM ها آشنا بشید تا تفاوت connected node ها و المنت هایی که به صورت markup نوشته شده اند رو بدونید، میتونید در موردش اینجا مطالعه کنید. در ادامه مقاله فرض بر اینه که شما با این مباحث آشنایی کافی دارید.خاصیت encapsulation ( یک المنت رفتارها و ویژگی های خصوصی خودش رو داره و از بقیه کد جداست ) به لطف Shadow DOM محقق میشه. Dom Shadow در واقع یک DOM مجزا و پنهان هست که به یک المنت الحاق (attach) میشه. میتونه برای خود یک DOM Tree تشکیل بده و با رفتارها و ویژگی های خاص خودش، DOM Tree خودش رو مدیریت کنه. میتونه اجازه دسترسی به داخل خودش رو به جاوااسکریپت های اجرایی صفحه بده میتونه نده.استفاده از فناوری Shadow DOM در واقع استفاده از یک مجموعه رابط های جاوااسکریپت (APIs) هست که به ما امکان attach کردن (الحاق شدن) یک shadow DOM به المنت معمولی و مدیریت DOM Tree مربوط بهش رو میده.فناوری HTML Templateالمنت های <template> و <slot> ما رو قادر میسازند که markup هایی بسازیم که رندر میشوند اما نمایش داده نمی شوند. محتوای اونها میتونه بارها و بارها به عنوان اساس و پایه custom element ها استفاده بشه.در ادامه من به دلیل نیازم، فقط template رو توضیح دادم ولی با slot کاری نداشتم. پس به همین بسنده میکنم که نقش slot در تمپلت شبیه نقش ng-content در تمپلت های انگولاره و مطالعه ش رو به خودت میسپارم.بخش دوم: چطوری به کمک Web Component این slide button رو بسازیم؟قدم اول : ساختن یک فایل index.html و یک فایل SlideButton.js هست و اضافه کردن SlideButton.js به فایل index.htmlبرای ساخت فایل SlideButton از ساختار اولیه Web Component استفاده میکنیم:کلاس HTMLElement و آبجکت window.costumElements از مجموعه امکاناتی هستند که برای ساخت Custom Elements در اختیار توسعه دهندگان وب قرار داده شده است. کلاس های متعددی در این مجموعه هستند مانند:HTMLAnchorElementHTMLParagraghElementHTMLHRElement....ارث بری از هر یک از این کلاس ها، باعث میشه وب کامپوننت شما، ویژگی ها و رفتارهای خاص المنت مربوطه رو به ارث ببره.همون طور که در خط آخر می بینیم، بعد از تعریف کلاس SlideButton ، نام تگ برای کامپوننت مون تعریف میکنیم و به مرورگر میفهمونیم که اون تگ، با کلاسی که تعریف کردیم مدیریت میشه.یه نگاهی هم به فایل index.html بندازیم:فایل SlideButton در انتهای فایل index.html اضافه شده و تگی که نام گذاری کردیمش قبل از فایل جاوااسکریپت استفاده شده. اگر ترتیب این دو خط رعایت نشه، با خطا مواجه میشیم.اما خروجی این دو فایل در نهایت به یک المنتی منجر میشه که هیچ نمایشی نداره ولی به عنوان المنت، برای مرورگر شناخته شده تلقی میشه:قدم دوم: اضافه کردن Shadow DOM به کامپوننت ( استفاده از فناوری دوم )با اضافه کردن یک خط، Shadow DOM رو به المنت الصاق میکنیم:خروجی HTML به شکل زیر در میاد:همون طور که میبینید، shadow root به المنت slide-button ما الصاق شده.تنظیم mode میتونه دو مقدار open و closed داشته باشه.مقدار open یعنی جاوااسکریپت دسترسی shadow tree رو داره.مقدار closed یعنی جاوااسکریپت دسترسی به shadow tree رو نداره.بعد از الحاق Shadow DOM ، همیشه به کمک عبارت this.shadowRoot به المنت ریشه و از طریق اون به کل Shadow Tree دسترسی داریم. در ادامه بارها از این دسترسی استفاده میکنیم.قدم سوم: نمایش کامپوننت به کمک template ( استفاده از فناوری سوم )وقتشه یک تمپلت برای المنت مون تعریف کنیم و بعد این تمپلت رو به عنوان DOM Tree اولیه برای Shadow Root تعریف کنیم. من یک تابع تعریف کردم به اسم #appendTemplate و کل قدم سوم رو داخل اون تابع انجام دادم. در مثال ها و نمونه ها معمولا این کار رو نمیکنند.در خط اول یک تمپلت به document اضافه کردم. سپس محتوای html و css مربوط به تمپلت رو بهش اضافه کردم. در انتها هم کل تمپلت رو به Shadow Root اضافه کردم و در نهایت Shadow Tree ساخته شد.اگر به تعریف HTML و CSS مربوط به Shadow Tree دقت کنید، می بینید که وب کامپوننت رو با یک کلاس on یا off باید استفاده کنیم. من مقدار پیش فرض براش در نظر نگرفتم که توسعه دهنده های دیگه متوجه بشن مقدار اولیه رو ست نکردند. بنابر این کلاس off رو به المنت slide-button در index.html اضافه میکنیم:و خروجی به شکل زیر در میاد:می بینیم که استایل ها و المنت های مربوطه در تمپلت به Shadow Root اضافه شدند، ولی تاثیری از کلاس off نیست!!!برای اینکه بتونیم به attribute کلاس دسترسی پیدا کنیم، یک getter بنام observedAttributes برای مشخص کردن اسم attribute هایی که کامپننت بهشون حساسه و callback مخصوص اطلاع از تغییر attribute ها بنام attributeChangedCallback رو به توابع کلاس مون اضافه میکنیم:به استفاده از this.shadowRoot برای دسترسی به Shadow Tree به کمک تابع querySelector دقت کنید. قبلا در موردش صحبت کرده بودیم.خروجی کدمون به صورت زیر در میاد:اگر کلاس اولیه کامپوننت رو به on تغییر بدیم، خروجی به صورت زیر دیده میشه:قدم چهارم: کلیک روی کل کامپوننت منجر به تغییر وضعیت بشهمیخوایم به کمک addEventListeren ، کامپوننت رو به کلیک حساس کنیم. این حساسیت رو موقع ساخت node ایجاد میکنیم و موقع حذف node از بین میبریم.برای این کار از دو callback استفاده میکنیم. یکی connectedCallback وقتی المنت به صورت node ساخته میشه و یکی هم disconnectedCallback وقتی node مربوط به المنت از داکیومنت حذف میشه:قدم پنجم: اطلاع از تغییرات داخل وب کامپوننتمیخوایم وقتی شرایط داخل وب کامپوننت تغییر کرد، بیرون از کامپوننت هم دیگران از این تغییر وضعیت مطلع بشن. بنابر این اول event مناسب رو پیاده میکنیم و بعد در index.html دریافتش میکنیم.برای پیاده سازی event از تابع this.dispatchEvent استفاده میکنیم که به لطف ارث بری از کلاس HTMLElement در اختیارمون گذاشته شده. البته من این کار رو داخل تابع خودم به اسم dispatchCustomEvent انجام دادم و این تابع رو در جای مناسبش صدا کردم:و دریافت این event رو در index.html پیاده سازی میکنیم. یک id برای المنت مون به اسم slidebutton تعریف میکنیم و به کمک document.getElementById بهش دسترسی پیدا میکنم و event رو دریافت میکنیم:نتیجه میشه خروجی زیر:مقدار detail در event آخرین وضعیت وب کامپوننت هست.به این ترتیب وب کامپوننت ما آماده است . حالا باید ببینیم چطوری میخوایم به انگولار اضافه ش کنیم.بخش سوم: اضافه کردن وب کامپوننت در انگولاراین بخش خیلی خیلی ساده است.یک دایرکتوری در اپ درست کردم در آدرس /src/app به اسم web-components و فایل وب کامپوننتم رو اونجا گذاشتم (slldebutton.js)بعد فایل وب کامپوننت رو در app.componen.ts ایمپورت کردم:در هر کامپوننتی که بخوام استفاده کنم، اول CUSTOM_ELEMENTS_SCHEMA رو به ماژولش اضافه میکنم:بعد میتونم در هر تمپلتی که نیاز دارم، المنت وب کامپوننتم رو وارد کنم.