Angular forms நிலையான HTML உடன் மிகச் சிறப்பாகச் செயல்படுகின்றன. input, textarea, மற்றும் select ஆகிய அனைத்தும் கூடுதல் முயற்சி இன்றி Reactive Forms-க்குள் எளிதாகப் பொருந்தும். இந்த framework அவற்றின் events, அவற்றின் values மற்றும் அவற்றின் states ஆகியவற்றைத் தானாகவே புரிந்துகொள்கிறது.

ஆனால் நவீன பயன்பாடுகள் (applications) பெரும்பாலும் நிலையான கூறுகளால் (standard elements) மட்டும் இயங்குவதில்லை. உங்களுக்கு ஒரு star rating widget, ஒரு composite date selector அல்லது ஒரு custom color picker தேவைப்படலாம். இவற்றில் ஒன்றை ஒரு form group-க்குள் சேர்த்தால், Angular அதை வெறும் HTML ஆக மட்டுமே கருதும். patchValue எதையும் செய்யாது. Validators அதைத் தவிர்க்கும். பயனர் அந்த control-உடன் எப்போது தொடர்பு கொள்கிறார் என்பது form-க்குத் தெரியாது, மேலும் form.disable() செய்தாலும் அந்த custom widget தொடர்ந்து இயங்கிக்கொண்டே இருக்கும்.

இந்தத் சிக்கலைத் தீர்ப்பதற்காகவே ControlValueAccessor உருவாக்கப்பட்டுள்ளது.

ControlValueAccessor உண்மையில் என்ன செய்கிறது

ControlValueAccessor என்பது ஒரு custom component-ஐ ஒரு முதன்மையான form அங்கமாக (first-class form citizen) மாற்றும் ஒரு ஒப்பந்தம் (contract) போன்றது. இது Angular Forms API மற்றும் உங்கள் சொந்த UI ஆகியவற்றுக்கு இடையே ஒரு மொழிபெயர்ப்பாளராக (translator) செயல்படுகிறது. நீங்கள் இதைச் சரியாகச் செயல்படுத்தியவுடன், form-ன் பார்வையில் உங்கள் component ஒரு native input-லிருந்து வேறுபட்டது என்று தெரியாது. ஒரு built-in element போலவே இது மதிப்புகளைப் பெறவும் (receive values), மாற்றங்களை அறிவிக்கவும் (emit changes), touches-ஐத் தெரிவிக்கவும் (report touches) மற்றும் disabled states-ஐப் பின்பற்றவும் முடியும்.

இந்த interface-க்கு நான்கு குறிப்பிட்ட முறைகள் (methods) தேவைப்படுகின்றன. ஒவ்வொன்றும் தொடர்புகொள்வதற்கான ஒரு தனித்துவமான திசையைக் கையாள்கிறது.

writeValue: Form இலிருந்து Component-க்கு

writeValue(obj) என்பது உள்ளே வரும் பாதையாகும் (inbound lane). Form model எப்போது புதுப்பிக்கப்பட்டு உங்கள் UI-க்குள் ஒரு புதிய மதிப்பைத் தள்ளத் தேவையாக இருக்கிறதோ, அப்போது Angular இந்த முறையை அழைக்கும். நீங்கள் ஒரு form group-ல் patchValue({ rating: 4 }) என்று அழைத்தால், அந்த 4 என்ற மதிப்பு writeValue மூலம் உங்கள் component-க்குள் வரும். நீங்கள் form-ஐ reset செய்தால், writeValue புதிய ஆரம்ப மதிப்பை அல்லது null-ஐப் பெறும். இந்த முறையின் உள்ளே உங்கள் வேலை, அந்த வரும் தரவை எடுத்து உங்கள் component-ன் உள் நிலைக்கு (internal state) பொருத்துவதாகும் (map). நீங்கள் ஒரு color picker-ஐ உருவாக்குகிறீர்கள் என்றால், writeValue என்பது #ff4400 போன்ற ஒரு hex string-ஐப் பெறும், மேலும் அந்த நிறம் தேர்ந்தெடுக்கப்பட்டதாகக் காட்ட உங்கள் view-வை நீங்கள் புதுப்பிக்க வேண்டும்.

இதில் ஒரு நடைமுறைச் சிக்கல் உள்ளது. குறிப்பாக dynamically render செய்யப்படும் components, dialogs அல்லது tabbed interfaces ஆகியவற்றிற்குள், உங்கள் view முழுமையாகத் தொடங்குவதற்கு முன்பே Angular writeValue-ஐ அழைக்கக்கூடும். உங்கள் component மிக விரைவாக DOM அல்லது child components-ஐத் தொடர முயன்றால், runtime errors ஏற்படலாம். ஒரு சிறந்த வழிமுறை என்னவென்றால், அந்த மதிப்பை ஒரு local property-யில் சேமித்து வைத்து, view initialized ஆன பிறகு அதைப் பயன்படுத்துவது அல்லது undefined child references-களுக்கு எதிராகப் பாதுகாப்பளிப்பதாகும். உங்கள் template நிலையானதாக (stable) இருக்கும்போது மட்டுமே writeValue இயங்கும் என்று ஒருபோதும் assumptions வைத்துக்கொள்ளாதீர்கள்.

registerOnChange: Component இலிருந்து Form-க்கு

registerOnChange(fn) என்பது வெளியே செல்லும் பாதையை (outbound lane) அமைக்கிறது. Angular உங்களுக்கு ஒரு callback function-ஐ வழங்கும், நீங்கள் அதன் reference-ஐ வைத்திருக்க வேண்டும். பயனர் உங்கள் component-க்குள் இருக்கும் மதிப்பை மாற்றும் ஒவ்வொரு முறையும், அந்த புதிய மதிப்பைக் கொண்டு நீங்கள் அந்த function-ஐ அழைக்க வேண்டும். ஒரு star rating component-ல், பயனர் மூன்றாவது நட்சத்திரத்தைக் கிளிக் செய்யும் போது, நீங்கள் சேமித்து வைக்கப்பட்ட callback-ஐ 3 என்ற மதிப்புடன் அழைக்க வேண்டும். அந்த அழைப்பு FormControl-க்குள் சென்று, model-ஐப் புதுப்பித்து, ஏதேனும் valueChanges subscriptions-களைத் தூண்டி, validators-களை மீண்டும் இயக்கும்.

இந்தப் படிநிலையைத் தவிர்ப்பது ஒரு form-ஐ அமைதியாகச் செயலிழக்கச் செய்வதற்கான மிகவும் பொதுவான வழியாகும். அந்த widget இயங்குவது போலத் தோன்றலாம். நட்சத்திரங்கள் ஒளிர்வதையோ, நிறங்கள் மாறுவதையோ அல்லது தேதிகள் நிரப்பப்படுவதையோ பயனர் காணலாம். ஆனால் form model ஒருபோதும் புதுப்பிக்கப்படாது. Validators தொடர்ந்து பழைய தரவை (stale data) மதிப்பீடு செய்யும். Submit handlers பழைய மதிப்புகளையே அனுப்பும். component வேலை செய்வது போலத் தோன்றும், ஆனால் form உண்மையில் எதையும் அறியாது. உங்கள் custom control பயனர் உள்ளீட்டைப் பெறுகிறது ஆனால் அதைச் சுற்றியுள்ள form அதைக் கவனிக்கவில்லை என்றால், இதற்கு இதுவே பெரும்பாலும் காரணமாக இருக்கும்.

registerOnTouched: தொடர்புகளைத் தெரிவித்தல் (Reporting Interaction)

Forms மதிப்புகளை மட்டும் கண்காணிக்கவில்லை. பயனர் ஒரு field-உடன் தொடர்பு கொண்டுள்ளனரா என்பதையும் அவை கண்காணிக்கின்றன. validation errors-களை எப்போது காண்பிப்பது என்பதைத் தீர்மானிக்க Angular 'touched state'-ஐப் பயன்படுத்துகிறது. ஒரு required text input, பக்கம் ஏற்றப்பட்ட உடனேயே சிவப்பாக மாறக்கூடாது. பயனர் tab key மூலம் வெளியேறினாலோ அல்லது வேறு எங்காவது கிளிக் செய்தாலோ மட்டுமே அது மாற வேண்டும்.

Native inputs இதை blur events மூலம் தானாகவே கையாளுகின்றன. Custom components அவ்வாறு செய்வதில்லை. இந்தத் தொடர்புகளை நீங்களே தெரிவிக்க registerOnTouched(fn) என்பதைப் பயன்படுத்த வேண்டும். Angular உங்களுக்கு மற்றொரு callback-ஐ வழங்கும்; பயனர் அந்த control-உடன் அர்த்தமுள்ள வகையில் தொடர்பு கொண்டுவிட்டார் என்று நீங்கள் கருதும் போது அதை அழைக்கலாம்.

சரியான நேரம் உங்கள் component-ஐப் பொறுத்தது. ஒரு text-போன்ற custom input-க்கு, நீங்கள் blur நிகழ்வின் போது அதை அழைக்கலாம். ஒரு star rating-க்கு, முதல் கிளிக் சரியான தருணமாக இருக்கலாம். ஒரு popover-ஐத் திறக்கும் color picker-க்கு, palette மூடும் வரை நீங்கள் காத்திருக்கலாம். இதில் முக்கியமானது ஒருமைப்பாடு (consistency). நீங்கள் touched callback-ஐ ஒருபோதும் அழைக்கவில்லை என்றால், Angular அந்த control-ஐ pristine என்றே தொடர்ந்து குறிக்கும். பயனர் எடிட்டிங்கை முடித்த பிறகும் validation errors மறைந்தே இருக்கும். இது குழப்பத்தையும் மோசமான பயனர் அனுபவத்தையும் (user experience) ஏற்படுத்தும்.

setDisabledState: படிவக் கட்டளைகளை மதித்தல்

வணிகத் தர்க்கத்தின் (business logic) அடிப்படையில் டைனமிக் படிவங்கள் (dynamic forms) தொடர்ந்து புலங்களை (fields) இயக்கி அல்லது முடக்கி (enable/disable) கொண்டிருக்கும். நீங்கள் ஒரு FormControl-இல் .disable() என்பதை அழைக்கும்போது, உங்கள் தனிப்பயன் கூறீடு (custom component) அதற்குப் பதிலளிக்க Angular தேவைப்படுகிறது. setDisabledState(isDisabled) ஒரு பூலியன் (boolean) மதிப்பைப் பெறுகிறது. அது true ஆக இருக்கும்போது, உங்கள் UI-ஐ நீங்கள் முடக்க வேண்டும்.

இதன் பொருள் வெறும் கிளிக் செய்வதைத் தவிர்ப்பது மட்டுமல்ல. நீங்கள் உட்புற பொத்தான்களை (internal buttons) முடக்க வேண்டும், கவனிக்கத்தக்க நிலைகளை (focusable states) நீக்க வேண்டும், மற்றும் குறைந்த ஒளிபுகுத்தன்மை (reduced opacity) அல்லது pointer-events: none போன்ற காட்சி மாற்றங்களைச் செய்ய வேண்டும். நீங்கள் இந்த முறையைப் புறக்கணித்தால், படிவ மாதிரி (form model) அது முடக்கப்பட்டதாகக் கூறினாலும், உங்கள் கூறீடு முழுமையாகச் செயல்படிக் கொண்டே இருக்கும். இது கண்டறிய கடினமான பிழைகளை (bugs) உருவாக்கும். படிவம் நிராகரிக்க வேண்டிய மதிப்புகளைப் பயனர்கள் மாற்ற முடியும். தவறான நிலைகளின் அடிப்படையில் சேமிக்கும் பொத்தான்கள் (Save buttons) இயங்கக்கூடும். படிவக் குழுவும் (form group) UI-யும் ஒன்றுக்கொன்று முரணாகச் செயல்படும்.

நன்கு கட்டமைக்கப்பட்ட ஒரு தனிப்பயன் கட்டுப்பாடு (custom control), setDisabledState-ஐ ஒரு கூடுதல் விஷயமாகப் பார்க்காமல், முதன்மையானத் தேவையாகக் கருதுகிறது.

பிழைத்திருத்த நேரத்தை (Debugging Time) வீணடிக்கும் தவறுகள்

இந்த இடைமுகத்திற்கு (interface) புதிய டெவலப்பர்களைத் தடுமாறச் செய்யும் சில தொடர்ச்சியான தவறுகள் உள்ளன.

மாற்றக் அழைப்பைக் (change callback) குறிப்பிடத் தவறுதல். உங்கள் கூறீடு அதன் உட்புற நிலையைப் புதுப்பிக்கலாம், ஆனால் படிவத்திற்கு அது தெரியாது. சரிபார்ப்பாளர்கள் (Validators) முடங்கிப் போவார்கள், மேலும் முதன்மைப் படிவங்கள் பழைய தரவைச் (stale data) சமர்ப்பிக்கும். பயனர் ஒரு புதிய மதிப்பை அளிக்கும் தருணத்திலேயே அந்தச் சேமிக்கப்பட்ட onChange செயல்பாட்டை (function) எப்போதும் இயக்கவும்.

தொடுதலுக்கான அழைப்பைத் (touched callback) தவிர்த்தல். அது இல்லையென்றால், Angular அந்தப் புலத்தை 'touched' என்று குறிக்காது. touched அல்லது dirty நிலைகளுடன் தொடர்புடைய பிழைச் செய்திகள் தோன்றாது. படிவம் சரியாகத் தெரிந்தாலும், என்ன தவறு என்று தெரியாமல் பயனர்கள் அதைச் சமர்ப்பிக்க முடியாமல் தவிப்பார்கள்.

முடக்கப்பட்ட நிலையைப் புறக்கணித்தல். காட்சி ரீதியாக இயங்கிக் கொண்டிருக்கும் ஒரு கட்டுப்பாடு, படிவம் முடக்கப்பட்டதாகக் கருதினால், அது நம்பகத்தன்மையற்ற சூழலை உருவாக்கும். பயனர் தொடர்ந்து தட்டச்சு செய்யலாம் அல்லது கிளிக் செய்யலாம், ஆனால் மாதிரி (model) அவற்றைப் புறக்கணிக்கும். அல்லது மோசமான நிலையில், ஒத்திசைவுச் சுழற்சிகளின் (sync cycles) போது மாதிரி அவர்களின் உள்ளீட்டைத் தன்னிச்சையாக மாற்றியமைக்கக்கூடும்.

NG_VALUE_ACCESSOR வழங்குநரை (provider) விடுத்தல். இது ஒரு அமைதியான கொலையாளி. நீங்கள் நான்கு முறைகளையும் செயல்படுத்திவிட்டு, உங்கள் கூறீட்டின் providers வரிசையில் NG_VALUE_ACCESSOR-ஐச் சேர்க்க மறந்தால், Angular உங்கள் கூறீட்டை ஒரு மதிப்பு அணுகியாக (value accessor) பதிவு செய்யாது. குறியீடு தொகுக்கப்படும் (compiles). காட்சித் தோன்றும் (renders). ஆனால் எதுவும் பிணைக்கப்படாது (bind). எந்தப் பிழைச் செய்தியும் வராது, ஆனால் கூறீடு படிவத்திற்கு முற்றிலும் வெளியே மிதந்து கொண்டிருக்கும். எப்போதும் இதை டெக்கரேட்டர் மெட்டாடேட்டாவில் (decorator metadata) சேர்க்கவும்.

Signals, Validators மற்றும் நவீன Angular

ControlValueAccessor என்பது பழைய (legacy) API அல்ல. இது நவீன Angular மேம்பாட்டிற்குச் சரியாகப் பொருந்துகிறது. நீங்கள் உட்புற நிலையை Signals, சாதாரணப் பண்புகள் (plain properties) அல்லது RxJS subjects மூலம் நிர்வகிக்கிறீர்களோ எதுவாக இருந்தாலும், அந்த நான்கு முறைகளே படிவத் தொகுதியுடனான (forms module) உங்கள் பொது ஒப்பந்தமாக இருக்கும். நீங்கள் writeValue-இல் மதிப்புகளைப் பெற்று, உங்கள் Signals அல்லது நிலையை மாற்றியமைத்து, Angular வழங்கும் அழைப்புகள் (callbacks) மூலம் வெளியிடுகிறீர்கள்.

நிலையான சரிபார்ப்பாளர்கள் (Standard validators) எந்த மாற்றமும் இன்றிச் செயல்படும். Validators.required, Validators.min, Validators.pattern மற்றும் தனிப்பயன் குறுக்கு-புல சரிபார்ப்பாளர்கள் (custom cross-field validators) அனைத்தும் உங்கள் CVA-ஆதரவு கொண்ட கூறீட்டை ஒரு இயல்பான உள்ளீடு (native input) போலவே மதிப்பிடும். படிவக் கட்டுப்பாடு ஒரு மதிப்பு மற்றும் ஒரு நிலையை மட்டுமே பார்க்கும். அந்த மதிப்பு ஒரு உரைப்பெட்டியிலிருந்தோ (text box) அல்லது கைவினை முறையில் உருவாக்கப்பட்ட மாதத் தேர்வுக் கருவியிலிருந்தோ (month-picker) வந்ததா என்பது அதைப் பொருட்படுத்தாது.

அந்தப் போர்ட்டபிலிட்டி (portability) தான் டிசைன் சிஸ்டம்கள் மற்றும் பகிரப்பட்ட UI நூலகங்களுக்கு (shared UI libraries) CVA-வை முக்கியமாக்குகிறது. ஒரு குழு ஒரு வலுவான தொலைபேசி எண் உள்ளீடு அல்லது கோப்புப் பதிவேற்ற விட்ஜெட்டை (file upload widget) உருவாக்குகிறது. அவர்கள் அந்த இடைமுகத்தை ஒருமுறை செயல்படுத்துகிறார்கள். நிறுவனத்திலுள்ள மற்ற அனைத்து குழுக்களும் எந்த கூடுதல் இணைப்பும் இன்றி அதைத் தங்கள் Reactive Forms-இல் பயன்படுத்திக் கொள்ளலாம். அந்த கூறீடு ஒவ்வொரு அம்சத் தொகுதியிலும் (feature module) கணிக்கக்கூடிய வகையில் செயல்படும், சீராகச் சரிபார்க்கும் மற்றும் நிலையாக முடங்கும்.

உண்மையான பயன்

ControlValueAccessor என்பது நேர்காணல் கேள்விகளுக்காக மனப்பாடம் செய்ய வேண்டிய மற்றொரு இடைமுகம் மட்டுமல்ல. இது உங்கள் தனிப்பயன் கூறீடுகள் இயல்பான HTML கூறுகளுக்கு இணையானவையாக Angular-இன் படிவச் சூழலில் (form ecosystem) பங்கேற்க உதவும் ஒரு பாலமாகும். இதைத் தேர்ச்சி பெறுவது என்பது உங்கள் விட்ஜெட்டிற்கும் படிவத்திற்கும் இடையிலான முழு உரையாடலையும் புரிந்துகொள்வதாகும்: மதிப்புகளைப் பெறுதல், மாற்றங்களை அறிவித்தல், தொடுதல்களைத் தெரிவித்தல் மற்றும் முடக்கப்பட்ட நிலைகளை மதித்தல். இந்த நான்கு விஷயங்களையும் சரியாகச் செய்துவிட்டால், அவற்றைப் பயன்படுத்தும் டெவலப்பர்களுக்குத் தெரியாதவாறு மிகச் சிக்கலான, மீண்டும் பயன்படுத்தக்கூடிய படிவக் கட்டுப்பாடுகளை நீங்கள் உருவாக்க முடியும். அதுவே ஒரு தொழில்முறை Angular கூறீட்டின் அடையாளம்.