Search code examples
angularckeditor

ckeditor angular ControlValueAccessor


I am trying to create a component that implements the ControlValueAccessor. The custom form control will implement a div that will become a ckeditor control that will then used Forms validator but It will not call the ngAfterViewInit on the custom-element and not create the ckeditor control. Should I have the ng-container tag?

view for custom-element

<ng-container *ngIf="controlGroup.controls.value">
    <Editor [formControl]="controlGroup.controls.value">
    </Editor>
</ng-container>

Component

import { Component, forwardRef, NgZone, EventEmitter, Inject, Input, Output, } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder, FormGroup, Validators, FormControl  } from '@angular/forms';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
    selector: 'Editor',
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => Editor),
            multi: true
        }
    ],
    template: `<div [attr.id]="editorId" contenteditable="true"
                style="width: 100% !important; overflow: hidden"
                class="form-control sentence-part-values-inline sentence-part-values-inline-textbox-number">
                </div>`,
})



export class Editor implements ControlValueAccessor {
    @Input() public model: OrderTemplateTool.Core.Sentences.EmeticRisks.RegimenDescriptorSentencePartModel;
    getModel(): OrderTemplateTool.Core.Sentences.EmeticRisks.RegimenDescriptorSentencePartModel {
        return this.model;
    }
    editorId: string;   

    @Output() public modelchange: EventEmitter<any> = new EventEmitter();
    public onModelChange(): void {
       // this.validate();
        this.modelchange.next(this.getModel());
    }

    constructor(@Inject(NgZone) private ngZone: NgZone) { this.editorId = "regimen-descriptor-" + (<any>window).newGuid();    }

    onChange: any = () => { }
    onTouch: any = () => { }
    val = ""

    set value(val) {
        if (val !== undefined && this.val !== val) {
            this.val = val
            this.onChange(val)
            this.onTouch(val)
        }

    }

    writeValue(value: any) {
        this.value = value
    }

    registerOnChange(fn: any) {
        this.onChange = fn
    }

    registerOnTouched(fn: any) {
        this.onTouch = fn
    }

    ngAfterViewInit() {


        var self = this;

        this.ngZone.runOutsideAngular(() => {
            console.log('Procssing ngZone');
            (<any>window).CKEDITOR.config.autoParagraph = false;
            console.log('ngAfterViewInit');
            console.log(this.editorId);
            if (!(<any>window).CKEDITOR.instances[this.editorId]) {
                (<any>window).CKEDITOR.disableAutoInline = true;
                (<any>window).CKEDITOR.inline(self.editorId, {
                    enterMode: 2,
                    toolbar: [
                        {
                            name: 'basicstyles', groups: ['basicstyles', 'cleanup'],
                            items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript']
                        },
                        {
                            name: 'source',
                            items: ['Sourcedialog']
                        }
                    ],
                    removeButtons: '',  //override default value from config.js of CKEDITOR,
                    extraPlugins: 'sourcedialog'
                });

                let editor = (<any>window).CKEDITOR.instances[self.editorId];
                console.log(editor);                  (<any>window).CKEDITOR.instances[this.editorId].setData(this.model.Value);
                                    (<any>window).CKEDITOR.instances[this.editorId].on("change", () => {
                    self.model.Value = (<any>window).CKEDITOR.instances[self.editorId].getData();    
                    var editorName = this.editorId;                        (<any>window).CKEDITOR.instances[editorName].updateElement();    
                    self.onModelChange();    
                });

                (<any>window).CKEDITOR.instances[this.editorId].on("instanceReady", (ev) => {
                    var editor = ev.editor;
                    editor.setReadOnly(false);
                });
            }
        });
    }

    ngOnDestroy() {
        this.ngZone.runOutsideAngular(() => {
            var editor = (<any>window).CKEDITOR.instances[this.editorId];
            if (editor) {
                editor.destroy(true);
            }
        });
    }
}

Solution

  • You can't use formControlName/formGroupName/formArrayName name without having a parent(or ancestor) formGroup directive.

    This means that you'll need to have something like this:

    <form [formGroup]="yourFormGroup">
     <!-- ... -->
     <custom-element formControlName="name"></custom-element>
     <!-- ... -->
    </form>
    

    However, if you don't want to worry about including it anywhere else, you can use the formControl directive:

    <custom-element [formControl]="yourFormControlInstance"></custom-element>