क्या आपने कभी सोचा है कि उपयोगकर्ता डेटा के माध्यम से कोणीय रूपों और HTML तत्वों का संयोजन कैसे करते हैं?
शुरुआत से ही हमने ControlValueAccessor
उपयोग किया, एक विशेष इंटरफ़ेस जिसमें केवल 4 विधियाँ शामिल ControlValueAccessor
:
interface ControlValueAccessor { writeValue(value: any): void registerOnChange(fn: (value: any) => void): void registerOnTouched(fn: () => void): void setDisabledState(isDisabled: boolean)?: void }
बॉक्स से बाहर, कोणीय में कई ऐसे एक्सेसर्स हैं: चेकबॉक्स और रेडियो बटन के लिए, इनपुट और चयन के लिए। हालाँकि, यदि आप एक ऐसी चैट विकसित कर रहे हैं, जिसमें आपको इटैलिक में लिखने का अवसर देने की आवश्यकता है, तो टेक्स्ट को बोल्ड बनाएं या कहें, इमोटिकॉन्स डालें, आप सबसे अधिक सामग्री स्वरूपित सामग्री बनाने के लिए contenteditable
विशेषता का उपयोग करेंगे।
कोणीय, contenteditable
साथ रूपों के उपयोग का समर्थन नहीं करता है, इसलिए आपको इसे स्वयं लिखना होगा।

ControlValueAccessor
जो निर्देश हम लिखते हैं, वह बिल्ट-इन एक्सेसर्स के समान काम करेगा - यह सामग्री के contenteditable
होने पर प्रतिक्रिया देगा। टेम्पलेट और प्रतिक्रियाशील रूपों के लिए निर्भरता इंजेक्शन के माध्यम से इसे प्राप्त करने के लिए, यह एक अंतर्निहित InjectionToken
प्रदान करने के लिए पर्याप्त है:
@Directive({ selector: '[contenteditable][formControlName],' + '[contenteditable][formControl],' + '[contenteditable][ngModel]', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ContenteditableValueAccessor), multi: true, }, ], }) export class ContenteditableValueAccessor implements ControlValueAccessor {
ControlValueAccessor
इंटरफ़ेस के लिए 3 विधियों की आवश्यकता है और इसमें 1 वैकल्पिक विधि है:
registerOnChange
- एक फ़ंक्शन आरंभीकरण के दौरान इस पद्धति में आएगा। इसे नए मूल्य के साथ कहा जाता है जब उपयोगकर्ता ने हमारे घटक में कुछ दर्ज किया है ताकि नया डेटा नियंत्रण में दर्ज हो।registerOnTouched
- एक फ़ंक्शन प्रारंभ के दौरान इस पद्धति में आएगा। इसे तब कहा जाना चाहिए जब नियंत्रण को touched
लिए उपयोगकर्ता ने हमारे घटक को छोड़ दिया। यह सत्यापन के लिए उपयोग किया जाता है।writeValue
- writeValue
- इस विधि को हमारे घटक को मान पास करने के लिए नियंत्रण द्वारा बुलाया जाएगा। इसका उपयोग तब किया जाता है जब मान बाहर के कोड के माध्यम से बदल जाता है ( setValue
या उस चर को ngModel
जो ngModel
से जुड़ा हुआ ngModel
), साथ ही प्रारंभिक मूल्य सेट करने के लिए।
यह ध्यान देने योग्य है कि, प्रतिक्रियाशील रूपों के विपरीत, ngModel
व्यवहार करता है - विशेष रूप से, इसमें प्रारंभिक मूल्य एक देरी के साथ शुरू होता है, और writeValue
पद्धति " writeValue
" दो बार, null
साथ पहली बार:
https://github.com/angular/angular/issues/14988
setDisabledState
(वैकल्पिक) - disabled
अवस्था में परिवर्तन होने पर इस विधि को नियंत्रण द्वारा बुलाया जाएगा। यद्यपि यह विधि वैकल्पिक है, लेकिन आपके घटक में इसका जवाब देना बेहतर है।
इंटरफ़ेस कार्यान्वयन
DOM तत्व के साथ काम करने के लिए, हमें Renderer2
आवश्यकता है और, वास्तव में, तत्व स्वयं, इसलिए उन्हें कंस्ट्रक्टर में जोड़ें:
constructor( @Inject(ElementRef) private readonly elementRef: ElementRef, @Inject(Renderer2) private readonly renderer: Renderer2, ) {}
हम उन तरीकों को सहेजते हैं जो नियंत्रण हमें कक्षा के निजी क्षेत्रों में पास करेंगे:
private onTouched = () => {}; private onChange: (value: string) => void = () => {}; registerOnChange(onChange: (value: string) => void) { this.onChange = onChange; } registerOnTouched(onTouched: () => void) { this.onTouched = onTouched; }
contenteditable
घटक के लिए disabled
अवस्था संपादन मोड को निष्क्रिय करने के बराबर है - contenteditable="false"
। बाहर से मान सेट करना तत्व के innerHTML
डोम की जगह के बराबर है, और उपयोगकर्ता द्वारा मूल्य को अपडेट करना और घटक को छोड़ कर संबंधित घटनाओं की सदस्यता लेकर निगरानी की जा सकती है:
@HostListener('input') onInput() { this.onChange(this.elementRef.nativeElement.innerHTML); } @HostListener('blur') onBlur() { this.onTouched(); } setDisabledState(disabled: boolean) { this.renderer.setAttribute( this.elementRef.nativeElement, 'contenteditable', String(!disabled), ); } writeValue(value: string) { this.renderer.setProperty( this.elementRef.nativeElement, 'innerHTML', value, ); }
वह, वास्तव में, सब है। यह कोणीय रूपों और contenteditable
तत्वों के काम के मूल कार्यान्वयन के लिए पर्याप्त है।
इंटरनेट एक्सप्लोरर
हालाँकि, एक युगल है लेकिन
सबसे पहले, फॉर्म का खाली प्रारंभिक मान null
, और IE11 में writeValue
बाद, हम टेम्पलेट में null
देखेंगे। कार्य को सही ढंग से लागू करने के लिए, हमें मूल्य को सामान्य करने की आवश्यकता है:
writeValue(value: string | null) { this.renderer.setProperty( this.elementRef.nativeElement, 'innerHTML', ContenteditableValueAccessor.processValue(value), ); } private static processValue(value: string | null): string { const processed = value || ''; return processed.trim() === '<br>' ? '' : processed; }
यहां हम निम्नलिखित स्थिति को भी संभालेंगे। कल्पना करें कि किसी तत्व की सामग्री में HTML टैग थे। यदि हम बस सब कुछ का चयन करते हैं और इसे हटाते हैं, तो यह अंदर खाली नहीं होगा - एक अकेला <br>
टैग वहां डाला जाएगा। खाली मान के साथ नियंत्रण को रोकना नहीं करने के लिए, हम इसे एक रिक्त स्ट्रिंग के रूप में मानेंगे।
दूसरी बात, इंटरनेट एक्सप्लोरर contenteditable
तत्वों के लिए input
घटनाओं का समर्थन नहीं करता है। हमें MutationObserver
का उपयोग करके फॉलबैक लागू करना होगा:
private readonly observer = new MutationObserver(() => { this.onChange( ContenteditableValueAccessor.processValue( this.elementRef.nativeElement.innerHTML, ), ); }); ngAfterViewInit() { this.observer.observe(this.elementRef.nativeElement, { characterData: true, childList: true, subtree: true, }); } ngOnDestroy() { this.observer.disconnect(); }
हम किसी विशिष्ट ब्राउज़र पर सत्यापन लागू नहीं करेंगे। इसके बजाय, हम तुरंत पहली input
घटना पर MutationObserver
को अक्षम करते हैं:
@HostListener('input') onInput() { this.observer.disconnect();
अब हमारा घटक IE11 में काम करता है और हम खुद से संतुष्ट हैं!
इंटरनेट एक्सप्लोरर! ಠ (ಠ 益 ಠ ಠ)
दुर्भाग्य से, IE11 को अभी पीछे नहीं छोड़ा गया है। जाहिरा तौर पर, MutationObserver
के काम में एक बग है। यदि सामग्री के तत्व के अंदर टैग हैं, उदाहरण के लिए, some <b>text</b>
, तो जब पाठ हटा रहा है, जो पूरे टैग (इस उदाहरण में शब्द text
) को हटाने की आवश्यकता होगी, पर्यवेक्षक के कॉलबैक को वास्तविक से पहले बुलाया जाएगा DOM में बदलाव!

दुर्भाग्य से, हमारे पास हार स्वीकार करने और setTimeout
उपयोग करने के अलावा कोई विकल्प नहीं है:
private readonly observer = new MutationObserver(() => { setTimeout(() => { this.onChange( ContenteditableValueAccessor.processValue( this.elementRef.nativeElement.innerHTML, ), ); }); });
निष्कर्ष
बशर्ते कि कोणीय को इंटरनेट एक्सप्लोरर के संस्करणों का समर्थन करना चाहिए 9, 10 और 11, यह स्पष्ट हो जाता है कि उन्होंने घर में contenteditable
साथ काम क्यों लागू नहीं किया।
इसके अलावा, आपको यह याद रखना होगा कि HTML में दुर्भावनापूर्ण कोड हो सकता है - इसलिए, साहसपूर्वक अज्ञात सामग्री न लें और इसे नियंत्रण में paste
करें, और उपयोगकर्ता इनपुट को paste
और drop
ईवेंट में जांचना आवश्यक है। इस लेख में वर्णित कोड कोणीय 4 और उच्चतर में काम करता है, जिसमें FormHooks
भी FormHooks
। यदि आप चाहें, तो आप Renderer
उपयोग कर सकते हैं, यदि आप Renderer
उपयोग करते हैं, तो Renderer
2 का नहीं। स्रोत कोड और npm पैकेज लिंक पर उपलब्ध हैं:
https://github.com/TinkoffCreditSystems/angular-contenteditable-accessor
https://www.npmjs.com/package/@tinkoff/angular-contenteditable-accessor
और यहाँ एक उदाहरण के साथ खेलते हैं:
https://stackblitz.com/edit/angular2-contenteditable-value-accessor