কাস্টম এলিমেন্ট সংক্রান্ত পেশাদার পদ্ধতি

কাস্টম এলিমেন্ট আপনাকে নিজের HTML ট্যাগ তৈরি করতে দেয়। এই চেকলিস্টে পেশাদার পদ্ধতিগুলি কভার করা হয়েছে যা আপনাকে খুব ভাল কোয়ালিটির এলিমেন্ট তৈরি করতে সাহায্য করবে।

কাস্টম এলিমেন্ট আপনাকে HTML এক্সটেন্ড করতে এবং আপনার নিজস্ব ট্যাগ সংজ্ঞায়িত করতে দেয়। এটি একটি অবিশ্বাস্য শক্তিশালী ফিচার, তবে এটি লো-লেভেল, যার অর্থ হল আপনার নিজস্ব এলিমেন্ট কীভাবে সবচেয়ে ভালভাবে প্রয়োগ করা যায় তা সবসময় স্পষ্ট নয়।

আপনাকে সেরা অভিজ্ঞতা তৈরি করতে সাহায্য করার জন্য আমরা এই চেকলিস্টটি তৈরি করেছি। এটি আমাদের মতে একটি ভাল আচরণ করা কাস্টম এলিমেন্ট হতে যা যা লাগে তা সব ভেঙে দেয়।

চেকলিস্ট

শ্যাডো DOM

স্টাইল এনক্যাপসুলেট করতে শ্যাডো রুট তৈরি করুন।

কেন? আপনার এলিমেন্টের শ্যাডো রুটে স্টাইল এনক্যাপসুলেট করলে, এটি যেখানেই ব্যবহার করা হোক না কেন, কাজ করবে। এটি বিশেষভাবে গুরুত্বপূর্ণ যদি কোনও ডেভেলপার আপনার এলিমেন্ট অন্য এলিমেন্টের শ্যাডো রুটের মধ্যে রাখতে চান। এটি এমনকি চেকবক্স বা রেডিও বোতামের মতো সাধারণ এলিমেন্টের ক্ষেত্রেও প্রযোজ্য। এমনও হতে পারে যে আপনার শ্যাডো রুটের মধ্যে থাকা একমাত্র কন্টেন্ট হল স্টাইল।
উদাহরণ <howto-checkbox> এলিমেন্ট।

কনস্ট্রাক্টরে আপনার শ্যাডো রুট তৈরি করুন।

কেন? কনস্ট্রাক্টর হল যখন আপনার এলিমেন্ট সম্পর্কে এক্সক্লুসিভ জ্ঞান থাকে। ইমপ্লিমেন্টেশন সংক্রান্ত বিবরণ সেট-আপ করার জন্য এটি একটি দারুণ সময়, যাতে অন্য কোনও এলিমেন্ট এটির সাথে মিশে না যায়। পরবর্তী কলব্যাকে এই কাজ করার অর্থ হল, connectedCallback-এর মতো, আপনাকে এমন পরিস্থিতি থেকে সুরক্ষিত থাকতে হবে যেখানে আপনার এলিমেন্ট আলাদা করা হয়েছে এবং তারপরে ডকুমেন্টে আবার অ্যাটাচ করা হয়েছে।
উদাহরণ <howto-checkbox> এলিমেন্ট।

এলিমেন্টটি তৈরি করে এমন যেকোনও চাইল্ডকে এর শ্যাডো রুটে রাখুন।

কেন? আপনার এলিমেন্ট দ্বারা তৈরি চিলড্রেন এর ইমপ্লিমেন্টেশনের অংশ এবং এটি ব্যক্তিগত হওয়া উচিত। শ্যাডো রুটের সুরক্ষা না থাকলে, বাইরের জাভাস্ক্রিপ্ট অনিচ্ছাকৃতভাবে এইসব চাইল্ড এলিমেন্টে হস্তক্ষেপ করতে পারে।
উদাহরণ <howto-tabs> এলিমেন্ট।

আপনার শ্যাডো DOM-এ লাইট DOM চিলড্রেন প্রোজেক্ট করতে <slot> ব্যবহার করুন

কেন? আপনার কম্পোনেন্টের ব্যবহারকারীদের HTML চিলড্রেন হিসেবে আপনার কম্পোনেন্টে কন্টেন্ট নির্দিষ্ট করার অনুমতি দিলে আপনার কম্পোনেন্ট আরও কম্পোজেবল হয়ে ওঠে। কোনও ব্রাউজারে কাস্টম এলিমেন্ট কাজ না করলে, নেস্টেড কন্টেন্ট উপলভ্য, দৃশ্যমান ও অ্যাক্সেসযোগ্য থাকে।
উদাহরণ <howto-tabs> এলিমেন্ট।

inline-এর ডিফল্ট স্টাইল পছন্দ না হলে, :host ডিসপ্লে স্টাইল (যেমন, block, inline-block, flex) সেট করুন।

কেন? কাস্টম এলিমেন্ট ডিফল্ট হিসেবে display: inline, তাই সেগুলির width বা height সেট করলে কোনও প্রভাব পড়বে না। এটি প্রায়শই ডেভেলপারদের কাছে অপ্রত্যাশিত হয় এবং এর ফলে পৃষ্ঠার লেআউট সম্পর্কিত সমস্যা হতে পারে। আপনি inline ডিসপ্লে পছন্দ না করলে, আপনাকে সবসময় ডিফল্ট display ভ্যালু সেট করতে হবে।
উদাহরণ <howto-checkbox> এলিমেন্ট।

এমন একটি :host ডিসপ্লে স্টাইল যোগ করুন যা লুকানো অ্যাট্রিবিউটকে সম্মান করে।

কেন? ডিফল্ট display স্টাইল সহ কাস্টম এলিমেন্ট, যেমন :host { display: block }, কম নির্দিষ্টতা সহ বিল্ট-ইন hidden অ্যাট্রিবিউট ওভাররাইড করবে। আপনি যদি আশা করেন যে আপনার এলিমেন্টে hidden অ্যাট্রিবিউট সেট করলে সেটি display: none হিসেবে রেন্ডার হবে, তাহলে এটি আপনাকে অবাক করতে পারে। ডিফল্ট display স্টাইল ছাড়াও, :host([hidden]) { display: none }-এর সাথে hidden-এর জন্য সহায়তা যোগ করুন।
উদাহরণ <howto-checkbox> এলিমেন্ট।

অ্যাট্রিবিউট ও প্রপার্টি

লেখক-সেট, গ্লোবাল অ্যাট্রিবিউট ওভাররাইড করবেন না।

কেন? গ্লোবাল অ্যাট্রিবিউট হল সেগুলি যা সব HTML এলিমেন্টে থাকে। কিছু উদাহরণ হল tabindex এবং role। কোনও কাস্টম এলিমেন্ট তার প্রাথমিক tabindex-কে ০-তে সেট করতে চাইতে পারে যাতে এটি কীবোর্ড ফোকাসযোগ্য হয়। তবে, আপনার এলিমেন্ট ব্যবহার করা ডেভেলপার এটিকে অন্য কোনও ভ্যালুতে সেট করেছেন কিনা তা আপনাকে সবসময় প্রথমে চেক করে নিতে হবে। যেমন, তারা যদি tabindex-কে -1-এ সেট করে থাকেন, তাহলে বুঝতে হবে যে তারা চান না যে এলিমেন্টটি ইন্টার‍্যাক্টিভ হোক।
উদাহরণ <howto-checkbox> এলিমেন্ট। এটি আরও ব্যাখ্যা করা হয়েছে পৃষ্ঠার লেখককে ওভাররাইড করবেন না।

অ্যাট্রিবিউট বা প্রপার্টি হিসেবে সবসময় প্রিমিটিভ ডেটা (স্ট্রিং, সংখ্যা, বুলিয়ান) গ্রহণ করুন।

কেন? কাস্টম এলিমেন্ট, তাদের বিল্ট-ইন কাউন্টারপার্টের মতো, কনফিগারযোগ্য হতে হবে। কনফিগারেশন অ্যাট্রিবিউটের মাধ্যমে ডিক্লারেটিভভাবে বা জাভাস্ক্রিপ্ট প্রপার্টির মাধ্যমে ইম্পারেটিভভাবে পাস করা যেতে পারে। সাধারণত প্রতিটি অ্যাট্রিবিউটকে সংশ্লিষ্ট প্রপার্টির সাথে লিঙ্ক করা উচিত।
উদাহরণ <howto-checkbox> এলিমেন্ট।

প্রিমিটিভ ডেটা অ্যাট্রিবিউট ও প্রপার্টি সিঙ্ক করে রাখার চেষ্টা করুন, যাতে প্রপার্টি থেকে অ্যাট্রিবিউট এবং অ্যাট্রিবিউট থেকে প্রপার্টিতে পরিবর্তন প্রতিফলিত হয়।

কেন? ব্যবহারকারী আপনার এলিমেন্টের সাথে কীভাবে ইন্টার‍্যাক্ট করবেন তা আপনি কখনই জানতে পারবেন না। তারা জাভাস্ক্রিপ্টে কোনও প্রপার্টি সেট করতে পারেন এবং তারপরে getAttribute()-এর মতো API ব্যবহার করে সেই ভ্যালু পড়ার প্রত্যাশা করতে পারেন। প্রতিটি অ্যাট্রিবিউটের যদি একটি সংশ্লিষ্ট প্রপার্টি থাকে এবং দুটিই প্রতিফলিত হয়, তাহলে ব্যবহারকারীদের জন্য আপনার এলিমেন্ট নিয়ে কাজ করা সহজ হবে। অন্যভাবে বলতে গেলে, setAttribute('foo', value) কল করলে, সংশ্লিষ্ট foo প্রপার্টিও সেট করতে হবে এবং এর উল্টোটাও করতে হবে। অবশ্যই, এই নিয়মের কিছু ব্যতিক্রম আছে। আপনার হাই ফ্রিকোয়েন্সি প্রপার্টি দেখানো উচিত নয়, যেমন currentTime ভিডিও প্লেয়ারে। আপনার যেটি ঠিক মনে হচ্ছে সেই সিদ্ধান্ত নিন। যদি মনে হয় যে কোনও ব্যবহারকারী কোনও প্রপার্টি বা অ্যাট্রিবিউটের সাথে ইন্টার‍্যাক্ট করবেন এবং এটি দেখাতে কোনও অসুবিধা নেই, তাহলে তা করুন।
উদাহরণ <howto-checkbox> এলিমেন্ট। এই বিষয়ে আরও জানতে রি-এন্ট্রেন্সি সংক্রান্ত সমস্যা এড়িয়ে চলুন নিবন্ধটি পড়ুন।

প্রপার্টি হিসেবে শুধু রিচ ডেটা (অবজেক্ট, অ্যারে) গ্রহণ করার চেষ্টা করুন।

কেন? সাধারণত, এমন কোনও বিল্ট-ইন HTML এলিমেন্টের উদাহরণ নেই যা তাদের অ্যাট্রিবিউটের মাধ্যমে রিচ ডেটা (প্লেন জাভাস্ক্রিপ্ট অবজেক্ট এবং অ্যারে) গ্রহণ করে। পরিবর্তে, মেথড কল বা প্রপার্টির মাধ্যমে রিচ ডেটা গ্রহণ করা হয়। অ্যাট্রিবিউট হিসেবে রিচ ডেটা গ্রহণ করার ক্ষেত্রে কিছু স্পষ্ট অসুবিধা আছে: একটি বড় অবজেক্টকে স্ট্রিংয়ে সিরিয়ালাইজ করা ব্যয়বহুল হতে পারে এবং এই স্ট্রিংয়ে রূপান্তর করার প্রসেসে যেকোনও অবজেক্ট রেফারেন্স হারিয়ে যাবে। যেমন, আপনি যদি এমন কোনও অবজেক্টকে স্ট্রিংয়ে পরিণত করেন যার সাথে অন্য কোনও অবজেক্ট বা DOM নোডের রেফারেন্স আছে, তাহলে সেই রেফারেন্স হারিয়ে যাবে।

অ্যাট্রিবিউটে রিচ ডেটা প্রপার্টি দেখাবেন না।

কেন? অ্যাট্রিবিউটে রিচ ডেটা প্রপার্টি দেখানো অপ্রয়োজনীয়ভাবে ব্যয়বহুল, একই জাভাস্ক্রিপ্ট অবজেক্ট সিরিয়ালাইজ ও ডিসিরিয়ালাইজ করতে হয়। আপনার যদি এমন কোনও ব্যবহারের কেস না থাকে যা শুধুমাত্র এই ফিচারের মাধ্যমে সমাধান করা যেতে পারে, তাহলে সম্ভবত এটি এড়িয়ে যাওয়াই ভালো।

এলিমেন্ট আপগ্রেড করার আগে সেট করা হয়েছে এমন প্রপার্টি চেক করে দেখুন।

কেন? আপনার এলিমেন্ট ব্যবহার করছেন এমন কোনও ডেভেলপার, এলিমেন্টের সংজ্ঞা লোড হওয়ার আগেই এলিমেন্টে কোনও প্রপার্টি সেট করার চেষ্টা করতে পারেন। এটি বিশেষ করে তখনই প্রযোজ্য হয় যখন ডেভেলপার এমন ফ্রেমওয়ার্ক ব্যবহার করেন যা কম্পোনেন্ট লোড করা, পৃষ্ঠায় স্ট্যাম্প করা এবং মডেলে তাদের প্রপার্টি বাইন্ড করার কাজ করে।
উদাহরণ <howto-checkbox> এলিমেন্ট। প্রপার্টি লেজি করুন লিঙ্কে আরও ব্যাখ্যা করা হয়েছে।

ক্লাস নিজে থেকে প্রয়োগ করবেন না।

কেন? যেসব এলিমেন্টের স্ট্যাটাস প্রকাশ করা প্রয়োজন, সেগুলি অ্যাট্রিবিউট ব্যবহার করে তা করতে হবে। সাধারণত class অ্যাট্রিবিউট আপনার এলিমেন্ট ব্যবহারকারী ডেভেলপারের মালিকানাধীন বলে বিবেচনা করা হয় এবং আপনি নিজে এতে লিখলে, অসাবধানতাবশত ডেভেলপার ক্লাসের উপর ওভাররাইট হয়ে যেতে পারে।

ইভেন্ট

ইন্টার্নাল কম্পোনেন্ট অ্যাক্টিভিটির উত্তরে ইভেন্ট ডিসপ্যাচ করুন।

কেন? আপনার কম্পোনেন্টে এমন প্রপার্টি থাকতে পারে যা অ্যাক্টিভিটির উত্তরে পরিবর্তিত হয়, যেটি শুধু আপনার কম্পোনেন্টই জানে, যেমন, কোনও টাইমার বা অ্যানিমেশন সম্পূর্ণ হলে অথবা কোনও রিসোর্স লোড হওয়া শেষ হলে। কম্পোনেন্টের স্টেট যে আলাদা তা হোস্টকে জানানোর জন্য এইসব পরিবর্তনের উত্তরে ইভেন্ট ডিসপ্যাচ করা সহায়ক হয়।

হোস্ট কোনও প্রপার্টি সেট করলে (নিম্নমুখী ডেটা ফ্লো) তার উত্তরে ইভেন্ট ডিসপ্যাচ করবেন না।

কেন? হোস্ট কোনও প্রপার্টি সেট করার উত্তরে ইভেন্ট ডিসপ্যাচ করা অপ্রয়োজনীয় (হোস্ট বর্তমান অবস্থা জানে কারণ এটি এখনই সেট করা হয়েছে)। কোনও হোস্ট প্রপার্টি সেট করার উত্তরে ইভেন্ট ডিসপ্যাচ করলে, ডেটা বাইন্ডিং সিস্টেমের সাথে অসীম লুপ তৈরি হতে পারে।
উদাহরণ <howto-checkbox> এলিমেন্ট।

ব্যাখ্যাকারী

পৃষ্ঠার লেখককে ওভাররাইড করবেন না

আপনার এলিমেন্ট ব্যবহার করছেন এমন কোনও ডেভেলপার হয়ত এর প্রাথমিক অবস্থার কিছু অংশ ওভাররাইড করতে চাইতে পারেন। যেমন, এর ARIA role বা ফোকাস করার ক্ষমতা tabindex-এর মাধ্যমে পরিবর্তন করা। আপনার নিজস্ব ভ্যালু প্রয়োগ করার আগে, এইসব এবং অন্য কোনও গ্লোবাল অ্যাট্রিবিউট সেট করা হয়েছে কিনা তা চেক করে দেখুন।

connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', 0);

প্রপার্টি লেজি করুন

কোনও ডেভেলপার আপনার এলিমেন্টের সংজ্ঞা লোড হওয়ার আগেই সেটির উপর কোনও প্রপার্টি সেট করার চেষ্টা করতে পারেন। ডেভেলপার যদি এমন ফ্রেমওয়ার্ক ব্যবহার করেন যা কম্পোনেন্ট লোড করা, পৃষ্ঠায় সেগুলি যোগ করা এবং মডেলের সাথে সেগুলির প্রপার্টি বাইন্ড করার কাজ করে, তাহলে এটি বিশেষভাবে প্রযোজ্য হয়।

নিচে দেওয়া উদাহরণে, Angular তার মডেলের isChecked প্রপার্টিকে চেকবক্সের checked প্রপার্টির সাথে ডিক্লেরেটিভভাবে বাইন্ড করছে। howto-checkbox-এর সংজ্ঞা ধীরে ধীরে লোড করা হলে, এলিমেন্ট আপগ্রেড করার আগে Angular, চেক করা প্রপার্টি সেট করার চেষ্টা করতে পারে।

<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>

কোনও কাস্টম এলিমেন্টের ইনস্ট্যান্সে আগে থেকেই কোনও প্রপার্টি সেট করা আছে কিনা তা চেক করে এই পরিস্থিতি সামলানো উচিত। <howto-checkbox> _upgradeProperty() নামক মেথড ব্যবহার করে এই প্যাটার্ন দেখায়।

connectedCallback() {
  ...
  this._upgradeProperty('checked');
}

_upgradeProperty(prop) {
  if (this.hasOwnProperty(prop)) {
    let value = this[prop];
    delete this[prop];
    this[prop] = value;
  }
}

_upgradeProperty() আপগ্রেড না করা ইনস্ট্যান্স থেকে ভ্যালু ক্যাপচার করে এবং প্রপার্টি মুছে দেয় যাতে এটি কাস্টম এলিমেন্টের নিজস্ব প্রপার্টি সেটারে ছায়া না ফেলে। এইভাবে, এলিমেন্টের সংজ্ঞা অবশেষে লোড হলে, এটি অবিলম্বে সঠিক অবস্থা দেখাতে পারে।

রি-এন্ট্রান্সি সংক্রান্ত সমস্যা এড়িয়ে চলুন

attributeChangedCallback() ব্যবহার করে কোনও অন্তর্নিহিত প্রপার্টিতে স্টেট রিফ্লেক্ট করার প্রলোভন হতে পারে, যেমন:

// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;
}

কিন্তু প্রপার্টি সেটারও অ্যাট্রিবিউটে প্রতিফলিত হলে এটি একটি অসীম লুপ তৈরি করতে পারে।

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

বিকল্প হিসেবে, প্রপার্টি সেটারকে অ্যাট্রিবিউটে রিফ্লেক্ট করার অনুমতি দিন এবং গেটারকে অ্যাট্রিবিউটের উপর ভিত্তি করে এর ভ্যালু নির্ধারণ করতে দিন।

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

get checked() {
  return this.hasAttribute('checked');
}

এই উদাহরণে, অ্যাট্রিবিউট যোগ করা বা সরানোর মাধ্যমে প্রপার্টিও সেট করা হবে।

অবশেষে, attributeChangedCallback()-কে সাইড এফেক্ট ম্যানেজ করার জন্য ব্যবহার করা যেতে পারে যেমন ARIA স্টেট প্রয়োগ করা।

attributeChangedCallback(name, oldValue, newValue) {
  const hasValue = newValue !== null;
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-checked', hasValue);
      break;
    ...
  }
}