Angular forms ప్రామాణిక HTMLతో అద్భుతంగా పనిచేస్తాయి. input, textarea, మరియు select అన్నీ అదనపు ప్రయత్నం లేకుండా Reactive Forms లోకి సులభంగా కలిసిపోతాయి. ఈ ఫ్రేమ్‌వర్క్ వాటి ఈవెంట్‌లు (events), వాటి విలువలు (values), మరియు వాటి స్టేట్‌లను (states) అర్థం చేసుకుంటుంది.

కానీ ఆధునిక అప్లికేషన్‌లు కేవలం ప్రామాణిక ఎలిమెంట్లతోనే సరిపోవు. మీకు ఒక స్టార్ రేటింగ్ విడ్జెట్ (star rating widget), ఒక కాంపోజిట్ డేట్ సెలెక్టర్ (composite date selector), లేదా ఒక కస్టమ్ కలర్ పిక్కర్ (custom color picker) అవసరం కావచ్చు. వీటిలో ఒకదాన్ని ఫామ్ గ్రూప్‌లో ఉపయోగిస్తే, Angular దానిని కేవలం ఒక సాధారణ HTML లాగా మాత్రమే చూస్తుంది. patchValue ఏమీ చేయదు. Validators దానిని పట్టించుకోవు. యూజర్ ఆ కంట్రోల్‌తో ఎప్పుడు ఇంటరాక్ట్ అవుతున్నారో ఫామ్‌కు తెలియదు, మరియు form.disable() చేసినప్పటికీ ఆ కస్టమ్ విడ్జెట్ పూర్తిగా ఇంటరాక్టివ్‌గానే ఉంటుంది.

ఈ సమస్యను పరిష్కరించడానికే ControlValueAccessor ఉద్దేశించబడింది.

ControlValueAccessor నిజంగా ఏమి చేస్తుంది

ControlValueAccessor అనేది ఒక కస్టమ్ కాంపోనెంట్‌ను ఫామ్‌లో ఒక ముఖ్యమైన భాగంగా (first-class form citizen) మార్చే ఒక ఒప్పందం (contract) వంటిది. ఇది Angular Forms API మరియు మీ స్వంత UI మధ్య ఒక అనువాదకుడిలా (translator) పనిచేస్తుంది. మీరు దీనిని సరిగ్గా అమలు చేసిన తర్వాత, ఫామ్ దృక్కోణం నుండి మీ కాంపోనెంట్ ఒక నేటివ్ ఇన్‌పుట్ (native input) లాగే కనిపిస్తుంది. ఇది బిల్ట్-ఇన్ ఎలిమెంట్ లాగే విలువలను స్వీకరించగలదు, మార్పులను తెలియజేయగలదు (emit changes), టచ్‌లను రిపోర్ట్ చేయగలదు మరియు డిసేబుల్డ్ స్టేట్‌లను (disabled states) గౌరవించగలదు.

ఈ ఇంటర్‌ఫేస్‌కు నాలుగు నిర్దిష్ట మెథడ్స్ అవసరం. ప్రతి ఒక్కటి కమ్యూనికేషన్ యొక్క ఒక ప్రత్యేక దిశను నిర్వహిస్తుంది.

writeValue: Form నుండి Component కి

writeValue(obj) అనేది ఇన్‌బౌండ్ లేన్ (inbound lane). ఫామ్ మోడల్ ఎప్పుడు అప్‌డేట్ అయినా మరియు మీ UIలోకి కొత్త విలువను పంపవలసి వచ్చినప్పుడు, Angular ఈ మెథడ్‌ను పిలుస్తుంది. మీరు ఒక ఫామ్ గ్రూప్‌పై patchValue({ rating: 4 }) అని పిలిస్తే, ఆ 4 అనే విలువ writeValue ద్వారా మీ కాంపోనెంట్ లోపలికి వస్తుంది. మీరు ఫామ్‌ను రీసెట్ చేస్తే, writeValue కొత్త ప్రారంభ విలువను లేదా null ను స్వీకరిస్తుంది. ఈ మెథడ్ లోపల మీ పని ఏమిటంటే, ఆ వచ్చే డేటాను తీసుకుని మీ కాంపోనెంట్ యొక్క అంతర్గత స్టేట్‌కు (internal state) మ్యాప్ చేయడం. మీరు ఒక కలర్ పిక్కర్‌ను నిర్మిస్తుంటే, writeValue #ff4400 వంటి హెక్స్ స్ట్రింగ్‌ను స్వీకరిస్తుంది, మరియు మీరు ఆ రంగు ఎంపిక చేయబడినట్లు చూపించడానికి మీ వ్యూ (view)ను అప్‌డేట్ చేయాలి.

ఇక్కడ ఒక చిన్న సమస్య ఉంది. ముఖ్యంగా డైనమిక్‌గా రెండర్ చేయబడే కాంపోనెంట్‌లు, డయలాగ్‌లు లేదా ట్యాబ్డ్ ఇంటర్‌ఫేస్‌లలో, మీ వ్యూ పూర్తిగా ఇనిషియలైజ్ (initialize) కావడానికి ముందే Angular writeValueను పిలవవచ్చు. మీ కాంపోనెంట్ చాలా త్వరగా DOM లేదా చైల్డ్ కాంపోనెంట్‌లను తాకడానికి ప్రయత్నిస్తే, రన్‌టైమ్ ఎర్రర్‌లు (runtime errors) వచ్చే అవకాశం ఉంది. ఒక మంచి పద్ధతి ఏమిటంటే, విలువను ఒక లోకల్ ప్రాపర్టీలో నిల్వ చేసి, వ్యూ ఇనిషియలైజ్ అయిన తర్వాత దానిని అప్లై చేయడం, లేదా అన్‌డిఫైన్డ్ చైల్డ్ రిఫరెన్స్‌ల నుండి రక్షణ పొందడం. మీ టెంప్లేట్ స్థిరంగా ఉన్నప్పుడు మాత్రమే writeValue ఫైర్ అవుతుందని ఎప్పుడూ అనుకోవద్దు.

registerOnChange: Component నుండి Form కి

registerOnChange(fn) అనేది అవుట్‌బౌండ్ లేన్ (outbound lane)ను సెటప్ చేస్తుంది. Angular మీకు ఒక కాల్‌బ్యాక్ ఫంక్షన్‌ను ఇస్తుంది, మీరు దాని రిఫరెన్స్‌ను ఉంచుకోవాలి. యూజర్ మీ కాంపోనెంట్ లోపల విలువను మార్చిన ప్రతిసారీ, మీరు ఆ ఫంక్షన్‌ను కొత్త విలువతో పిలవాలి. ఒక స్టార్ రేటింగ్ కాంపోనెంట్‌లో, యూజర్ మూడవ స్టార్‌ను క్లిక్ చేసినప్పుడు, మీరు నిల్వ చేసిన కాల్‌బ్యాక్‌ను 3 తో ఇన్వోక్ చేస్తారు. ఆ కాల్ తిరిగి FormControl లోకి వెళ్లి, మోడల్‌ను అప్‌డేట్ చేస్తుంది, ఏదైనా valueChanges సబ్‌స్క్రిప్షన్‌లను ట్రిగ్గర్ చేస్తుంది మరియు వాలిడేటర్లను మళ్ళీ రన్ చేస్తుంది.

ఈ దశను వదిలేయడం అనేది ఫామ్‌ను నిశ్శబ్దంగా విఫలం చేయడానికి అత్యంత సాధారణ మార్గం. విడ్జెట్ పని చేస్తున్నట్లు అనిపించవచ్చు. యూజర్ స్టార్‌లు వెలగడం, రంగులు మారడం లేదా తేదీలు నింపబడటం చూడవచ్చు. కానీ ఫామ్ మోడల్ ఎప్పటికీ అప్‌డేట్ కాదు. వాలిడేటర్లు పాత డేటాను (stale data) మాత్రమే పరిశీలిస్తూ ఉంటాయి. సబ్మిట్ హ్యాండ్లర్లు పాత విలువలను పంపుతాయి. కాంపోనెంట్ పనిచేస్తున్నట్లు కనిపిస్తుంది, కానీ ఫామ్ మాత్రం ఏమీ తెలియని స్థితిలో ఉంటుంది. మీ కస్టమ్ కంట్రోల్ యూజర్ ఇన్‌పుట్‌ను స్వీకరిస్తున్నప్పటికీ, చుట్టూ ఉన్న ఫామ్ దానిని గమనించకపోతే, దానికి కారణం దాదాపు ఎప్పుడూ ఇదే.

registerOnTouched: ఇంటరాక్షన్‌ను రిపోర్ట్ చేయడం

ఫామ్‌లు కేవలం విలువలను మాత్రమే ట్రాక్ చేయవు. యూజర్ ఒక ఫీల్డ్‌తో ఇంటరాక్ట్ అయ్యారా లేదా అనేది కూడా అవి ట్రాక్ చేస్తాయి. వాలిడేషన్ ఎర్రర్‌లను ఎప్పుడు చూపించాలో నిర్ణయించడానికి Angular 'touched state'ను ఉపయోగిస్తుంది. ఒక 'required' టెక్స్ట్ ఇన్‌పుట్ పేజీ లోడ్ అయిన వెంటనే ఎరుపు రంగులోకి మారకూడదు. యూజర్ ట్యాబ్ (tab) చేసి వెళ్ళే వరకు లేదా వేరే చోట క్లిక్ చేసే వరకు అది వేచి ఉండాలి.

నేటివ్ ఇన్‌పుట్‌లు బ్లర్ ఈవెంట్‌ల (blur events) ద్వారా దీనిని ఆటోమేటిక్‌గా నిర్వహిస్తాయి. కస్టమ్ కాంపోనెంట్‌లు అలా చేయవు. ఈ ఇంటరాక్షన్‌లను మీరే రిపోర్ట్ చేయడానికి మీరు registerOnTouched(fn)ను ఉపయోగించాలి. Angular మీకు మరొక కాల్‌బ్యాక్‌ను ఇస్తుంది; యూజర్ ఆ కంట్రోల్‌తో అర్థవంతంగా ఇంటరాక్ట్ అయ్యారని మీరు భావించినప్పుడు దానిని పిలవాలి.

దీని ఖచ్చితమైన సమయం మీ కాంపోనెంట్‌పై ఆధారపడి ఉంటుంది. టెక్స్ట్ లాంటి కస్టమ్ ఇన్‌పుట్ కోసం, మీరు బ్లర్ (blur) సమయంలో దానిని పిలవవచ్చు. స్టార్ రేటింగ్ కోసం, మొదటి క్లిక్ సరైన సమయం కావచ్చు. పాపొవర్ (popover) ఓపెన్ చేసే కలర్ పిక్కర్ కోసం, మీరు ప్యాలెట్ క్లోజ్ అయ్యే వరకు వేచి ఉండవచ్చు. ఇక్కడ ముఖ్యమైనది స్థిరత్వం (consistency). మీరు ఎప్పుడూ 'touched callback'ను పిలవకపోతే, Angular ఆ కంట్రోల్‌ను 'pristine' గానే పరిగణిస్తుంది. యూజర్ ఎడిటింగ్ పూర్తి చేసిన తర్వాత కూడా వాలిడేషన్ ఎర్రర్‌లు కనిపించవు. ఇది గందరగోళానికి మరియు అధ్వాన్నమైన యూజర్ ఎక్స్‌పీరియన్స్ (user experience)కు దారితీస్తుంది.

setDisabledState: ఫారమ్ కమాండ్స్‌ను గౌరవించడం

డైనమిక్ ఫారమ్‌లు బిజినెస్ లాజిక్ ఆధారంగా ఫీల్డ్‌లను నిరంతరం ఎనేబుల్ మరియు డిసేబుల్ చేస్తూ ఉంటాయి. మీరు ఒక FormControl పై .disable()ని కాల్ చేసినప్పుడు, మీ కస్టమ్ కాంపోనెంట్ దానికి స్పందించాల్సి ఉంటుంది. setDisabledState(isDisabled) ఒక బూలియన్ (boolean) విలువను స్వీకరిస్తుంది. అది true అయినప్పుడు, మీరు మీ UIని లాక్ చేయాలి.

దీని అర్థం కేవలం క్లిక్‌లను విస్మరించడం మాత్రమే కాదు. మీరు అంతర్గత బటన్‌లను డిసేబుల్ చేయాలి, ఫోకస్ చేయగల (focusable) స్టేట్‌లను తొలగించాలి మరియు తక్కువ ఒపాసిటీ (reduced opacity) లేదా pointer-events: none వంటి విజువల్ ట్రీట్‌మెంట్లను వర్తింపజేయాలి. మీరు ఈ మెథడ్‌ను విస్మరిస్తే, ఫారమ్ మోడల్ అది డిసేబుల్ అయిందని చెప్పినప్పటికీ, మీ కాంపోనెంట్ పూర్తిగా ఇంటరాక్టివ్‌గా ఉంటుంది. ఇది కనిపెట్టడానికి కష్టమైన బగ్‌లను (bugs) సృష్టిస్తుంది. ఫారమ్ తిరస్కరించాల్సిన విలువలను వినియోగదారులు మార్చగలుగుతారు. ఇన్వాలిడ్ స్టేట్‌ల ఆధారంగా సేవ్ బటన్‌లు ఎనేబుల్ కావచ్చు. దీనివల్ల ఫారమ్ గ్రూప్ మరియు UI మధ్య తేడా వస్తుంది.

చక్కగా నిర్మించబడిన కస్టమ్ కంట్రోల్ setDisabledStateను ఒక ప్రాథమిక అవసరంగా పరిగణిస్తుంది, కేవలం అదనపు అంశంగా కాదు.

డీబగ్గింగ్ సమయాన్ని వృధా చేసే తప్పులు

ఈ ఇంటర్‌ఫేస్‌కు కొత్తగా వచ్చిన డెవలపర్లు చేసే కొన్ని పునరావృతమయ్యే తప్పులు ఇక్కడ ఉన్నాయి.

change callbackని కాల్ చేయడం మర్చిపోవడం. మీ కాంపోనెంట్ దాని అంతర్గత స్టేట్‌ను అప్‌డేట్ చేస్తుంది, కానీ ఫారమ్‌కు దాని గురించి తెలియదు. దీనివల్ల వాలిడేటర్లు (Validators) ఆగిపోతాయి మరియు పేరెంట్ ఫారమ్‌లు పాత డేటాను (stale data) సబ్మిట్ చేస్తాయి. వినియోగదారుడు కొత్త విలువను నమోదు చేసిన వెంటనే ఆ స్టోర్ చేయబడిన onChange ఫంక్షన్‌ను ఎల్లప్పుడూ కాల్ చేయండి.

touched callbackని వదిలేయడం. అది లేకపోతే, Angular ఆ కంట్రోల్‌ను 'touched'గా గుర్తించదు. touched లేదా dirty స్టేట్‌లతో అనుసంధానించబడిన ఎర్రర్ మెసేజ్‌లు కనిపించవు. వినియోగదారులు ఫారమ్ సరిగ్గా ఉన్నట్లు కనిపిస్తున్నా, అది సబ్మిట్ కాకపోవడాన్ని చూస్తూ, అసలు తప్పు ఎక్కడ ఉందో తెలియక ఇబ్బంది పడతారు.

disabled స్టేట్‌ను నిర్లక్ష్యం చేయడం. విజువల్‌గా ఎనేబుల్ అయి ఉన్నా, ఫారమ్ అది డిసేబుల్ అయిందని భావిస్తే అది నమ్మకాన్ని దెబ్బతీస్తుంది. వినియోగదారుడు టైప్ చేయడం లేదా క్లిక్ చేయడం కొనసాగించవచ్చు, కానీ మోడల్ వారిని విస్మరిస్తుంది. లేదా ఇంకా దారుణంగా చెప్పాలంటే, సింక్ సైకిల్స్ (sync cycles) సమయంలో మోడల్ వారి ఇన్‌పుట్‌ను అప్పుడప్పుడు ఓవర్‌రైట్ చేస్తుంది.

NG_VALUE_ACCESSOR ప్రొవైడర్‌ను మర్చిపోవడం. ఇది ఒక నిశ్శబ్ద హంతకుడు. మీరు నాలుగు మెథడ్‌లను ఇంప్లిమెంట్ చేసినప్పటికీ, మీ కాంపోనెంట్ యొక్క providers అర్రేలో NG_VALUE_ACCESSORను జోడించడం మర్చిపోతే, Angular మీ కాంపోనెంట్‌ను వాల్యూ యాక్సెసర్‌గా ఎప్పటికీ నమోదు చేయదు. కోడ్ కంపైల్ అవుతుంది. వ్యూ రెండర్ అవుతుంది. కానీ ఏదీ బైండ్ (bind) అవ్వదు. ఎటువంటి ఎర్రర్ మెసేజ్ రాదు, కేవలం కాంపోనెంట్ ఫారమ్ నుండి పూర్తిగా విడిగా ఉంటుంది. దీనిని ఎల్లప్పుడూ డెకరేటర్ మెటాడేటాలో చేర్చండి.

Signals, Validators, మరియు మోడ్రన్ Angular

ControlValueAccessor అనేది పాత (legacy) API కాదు. ఇది మోడ్రన్ Angular డెవలప్‌మెంట్‌కు చక్కగా సరిపోతుంది. మీరు అంతర్గత స్టేట్‌ను Signals, సాధారణ ప్రాపర్టీలు లేదా RxJS subjectsతో నిర్వహించినా, ఆ నాలుగు మెథడ్‌లు ఫారమ్స్ మాడ్యూల్‌తో మీకున్న పబ్లిక్ కాంట్రాక్ట్‌గా ఉంటాయి. మీరు writeValueలో విలువలను తీసుకుంటారు, మీ Signals లేదా స్టేట్‌ను మారుస్తారు (mutate), మరియు Angular అందించే కాల్‌బ్యాక్‌ల ద్వారా వాటిని ఎమిట్ (emit) చేస్తారు.

స్టాండర్డ్ వాలిడేటర్లు ఎటువంటి మార్పులు లేకుండా పనిచేస్తాయి. Validators.required, Validators.min, Validators.pattern, మరియు కస్టమ్ క్రాస్-ఫీల్డ్ వాలిడేటర్లు అన్నీ మీ CVA-బ్యాక్డ్ కాంపోనెంట్‌ను నేటివ్ ఇన్‌పుట్‌లాగే అంచనా వేస్తాయి. ఫారమ్ కంట్రోల్ కేవలం ఒక విలువను మరియు స్టేట్‌ను మాత్రమే చూస్తుంది. ఆ విలువ టెక్స్ట్ బాక్స్ నుండి వచ్చిందా లేదా మీరు స్వయంగా తయారు చేసిన మంత్-పికర్ (month-picker) నుండి వచ్చిందా అన్న దానితో దానికి సంబంధం లేదు.

ఆ పోర్టబిలిటీ (portability) వల్లే డిజైన్ సిస్టమ్స్ మరియు షేర్డ్ UI లైబ్రరీలకు CVA చాలా ముఖ్యం. ఒక టీమ్ ఒక పటిష్టమైన ఫోన్-నంబర్ ఇన్‌పుట్ లేదా ఫైల్ అప్‌లోడ్ విడ్జెట్‌ను నిర్మిస్తుంది. వారు ఆ ఇంటర్‌ఫేస్‌ను ఒక్కసారి ఇంప్లిమెంట్ చేస్తే సరిపోతుంది. సంస్థలోని మిగిలిన ప్రతి టీమ్ ఎటువంటి అదనపు వైరింగ్ లేకుండా దానిని తమ Reactive Formsలో వాడుకోవచ్చు. ఆ కాంపోనెంట్ ప్రతి ఫీచర్ మాడ్యూల్‌లోనూ ఊహించిన విధంగా ప్రవర్తిస్తుంది, ఏకరీతిగా వాలిడేట్ అవుతుంది మరియు స్థిరంగా డిసేబుల్ అవుతుంది.

అసలైన సారాంశం

ControlValueAccessor అనేది ఇంటర్వ్యూ ప్రశ్నల కోసం గుర్తుంచుకోవాల్సిన మరో ఇంటర్‌ఫేస్ మాత్రమే కాదు. ఇది మీ కస్టమ్ కాంపోనెంట్‌లు నేటివ్ HTML ఎలిమెంట్లతో సమానంగా Angular ఫారమ్ ఎకోసిస్టమ్‌లో పాల్గొనేలా చేసే ఒక వంతెన. దీనిని ప్రావీణ్యం సంపాదించడం అంటే మీ విడ్జెట్ మరియు ఫారమ్ మధ్య జరిగే పూర్తి సంభాషణను అర్థం చేసుకోవడం: విలువలను స్వీకరించడం, మార్పులను నివేదించడం, టచ్‌లను తెలియజేయడం మరియు డిసేబుల్ స్టేట్‌లను గౌరవించడం. ఈ నాలుగు అంశాలను సరిగ్గా అర్థం చేసుకుంటే, వాటిని ఉపయోగించే డెవలపర్‌లకు అవి అదృశ్యంగా అనిపించేలా సంక్లిష్టమైన, పునర్వినియోగపరచదగిన (reusable) ఫారమ్ కంట్రోల్‌లను మీరు నిర్మించగలరు. అదే ఒక ప్రొఫెషనల్ Angular కాంపోనెంట్ యొక్క లక్షణం.