How to override property of base class in subclass properly in angular / typescript? ngx-quill and material

2.7k views Asked by At

I am trying to implement a material version of ngx-quill by following the example by the author here: ngx-quill-example

The problem is I am getting the following error in a file (mat-quill-base.ts) that I copied directly from the example repository:

'disabled' is defined as a property in class 'CanUpdateErrorState & CanDisable & MatQuillBase', but is overridden here in '_MatQuillBase' as an accessor.

I get two more of the same error for placeholder and required.

These properties need to be overridden to account for the changes required to run a material version of the ngx-quill editor.

See: stack blitz demo

Here's the relevant code for reference

mat-quill-base.ts

import {
    Directive,
    AfterViewInit,
    ElementRef,
    HostBinding,
    Inject,
    Input,
    NgZone,
    OnChanges,
    OnDestroy,
    Optional,
    PLATFORM_ID,
    Renderer2,
    Self
} from '@angular/core'
import { DOCUMENT } from '@angular/common'
import {
    ControlValueAccessor,
    FormGroupDirective,
    NgControl,
    NgForm,
    Validator
} from '@angular/forms'
import { DomSanitizer } from '@angular/platform-browser'
import { coerceBooleanProperty } from '@angular/cdk/coercion'
import {
    CanDisable,
    CanDisableCtor,
    CanUpdateErrorState,
    CanUpdateErrorStateCtor,
    ErrorStateMatcher,
    mixinDisabled,
    mixinErrorState
} from '@angular/material/core'
import { HasErrorState } from '@angular/material/core/common-behaviors/error-state'
import { MatFormFieldControl } from '@angular/material/form-field'
import { QuillEditorBase, QuillService } from 'ngx-quill'

// Boilerplate for applying mixins to _MatQuillBase
class MatQuillBase extends QuillEditorBase
{
    constructor(
    public _defaultErrorStateMatcher: ErrorStateMatcher,
    public _parentForm: NgForm,
    public _parentFormGroup: FormGroupDirective,
    public ngControl: NgControl,
    elementRef: ElementRef,
    domSanitizer: DomSanitizer,
    doc: any,
    platformId: any,
    renderer: Renderer2,
    zone: NgZone,
    service: QuillService
    ) {
    super(
        elementRef, domSanitizer, doc, platformId,
        renderer, zone, service
    )
    }
}

const _MatQuillMixinBase: CanUpdateErrorStateCtor & CanDisableCtor & typeof MatQuillBase =
    mixinErrorState(mixinDisabled(MatQuillBase))

@Directive()
export abstract class _MatQuillBase extends _MatQuillMixinBase
    implements AfterViewInit, CanDisable, CanUpdateErrorState,
    ControlValueAccessor, HasErrorState,
    MatFormFieldControl<any>, OnChanges,
    OnDestroy, Validator
{
    abstract controlType: string
    focused = false
    abstract id: string

    constructor(
    defaultErrorStateMatcher: ErrorStateMatcher,
    @Optional() parentForm: NgForm,
    @Optional() parentFormGroup: FormGroupDirective,
    @Optional() @Self() public ngControl: NgControl,
    elementRef: ElementRef,
    domSanitizer: DomSanitizer,
    @Inject(DOCUMENT) doc: any,
    @Inject(PLATFORM_ID) platformId: any,
    renderer: Renderer2,
    zone: NgZone,
    service: QuillService
    ) {
    super(
        defaultErrorStateMatcher, parentForm, parentFormGroup, ngControl,
        elementRef, domSanitizer, doc, platformId, renderer, zone, service
    )

    if (this.ngControl != null) {
        this.ngControl.valueAccessor = this;
    }

    this.onBlur.subscribe(() => {
        this.focused = false
        this.stateChanges.next()
    })
    this.onFocus.subscribe(() => {
        this.focused = true
        this.stateChanges.next()
    })
    }

    /*
    * GETTERS & SETTERS
    */

    @Input()
    get disabled(): boolean
    {
    if (this.ngControl && this.ngControl.disabled !== null) {
        return this.ngControl.disabled
    }
    return this._disabled
    }
    set disabled(value: boolean)
    {
    this._disabled = coerceBooleanProperty(value)

    // Browsers may not fire the blur event if the input is disabled too quickly.
    // Reset from here to ensure that the element doesn't become stuck.
    if (this.focused) {
        this.focused = false
        this.stateChanges.next()
    }
    }
    protected _disabled = false

    get empty() {
    return coerceBooleanProperty(this.value)
    }

    @Input()
    get placeholder(): string { return this._placeholder }
    set placeholder(value: string) {
    this._placeholder = value
    this.stateChanges.next()
    }
    protected _placeholder: string

    @Input()
    get required(): boolean { return this._required }
    set required(value: boolean) {
    this._required = coerceBooleanProperty(value)
    }
    protected _required = false

    @HostBinding('class.floating')
    get shouldLabelFloat() {
    return this.focused || !this.empty
    }

    get value(): any
    {
    try {
        return this.valueGetter(this.quillEditor, this.editorElem!)
    } catch (e) {
        return
    }
    }
    set value(value: any) {
    this.writeValue(value)
    this.stateChanges.next()
    }

    /*
    * METHODS
    */

    blur() {
    (this.editorElem.childNodes as NodeListOf<HTMLElement>)[0]['blur']()
    }

    focus() {
    this.quillEditor.focus()
    }

    @HostBinding('attr.aria-describedby') _describedBy = ''
    setDescribedByIds(ids: string[]) {
    this._describedBy = ids.join(' ')
    }

    onContainerClick(event: MouseEvent)
    {
    if (!this.focused) {
        this.quillEditor.focus()
    }
    }

    static ngAcceptInputType_disabled: boolean | string | null | undefined
    static ngAcceptInputType_required: boolean | string | null | undefined
}

disabled, required, and placeholder are all defined in the QuillEditorBase class. This is from the ngx-quill node module:

export declare abstract class QuillEditorBase implements AfterViewInit, ControlValueAccessor, OnChanges, OnDestroy, Validator {
    elementRef: ElementRef;
    protected domSanitizer: DomSanitizer;
    protected doc: any;
    protected platformId: any;
    protected renderer: Renderer2;
    protected zone: NgZone;
    protected service: QuillService;
    quillEditor: QuillType;
    editorElem: HTMLElement;
    content: any;
    format?: 'object' | 'html' | 'text' | 'json';
    theme?: string;
    modules?: QuillModules;
    debug?: 'warn' | 'log' | 'error' | false;
    readOnly?: boolean;
    placeholder?: string;
    maxLength?: number;
    minLength?: number;
    required: boolean;
    formats?: string[] | null;
    customToolbarPosition: 'top' | 'bottom';
    sanitize: boolean;
    styles: any;
    strict: boolean;
    scrollingContainer?: HTMLElement | string | null;
    bounds?: HTMLElement | string;
    customOptions: CustomOption[];
    customModules: CustomModule[];
    trackChanges?: 'user' | 'all';
    preserveWhitespace: boolean;
    classes?: string;
    trimOnValidation: boolean;
    onEditorCreated: EventEmitter<any>;
    onEditorChanged: EventEmitter<EditorChangeContent | EditorChangeSelection>;
    onContentChanged: EventEmitter<ContentChange>;
    onSelectionChanged: EventEmitter<SelectionChange>;
    onFocus: EventEmitter<Focus>;
    onBlur: EventEmitter<Blur>;
    disabled: boolean;
    onModelChange: (modelValue?: any) => void;
    onModelTouched: () => void;
    onValidatorChanged: () => void;
    constructor(elementRef: ElementRef, domSanitizer: DomSanitizer, doc: any, platformId: any, renderer: Renderer2, zone: NgZone, service: QuillService);
    static normalizeClassNames(classes: string): string[];
    valueGetter: (quillEditor: QuillType, editorElement: HTMLElement) => string | any;
    valueSetter: (quillEditor: QuillType, value: any) => any;
    ngAfterViewInit(): Promise<void>;
    selectionChangeHandler: (range: Range | null, oldRange: Range | null, source: string) => void;
    textChangeHandler: (delta: Delta, oldDelta: Delta, source: string) => void;
    editorChangeHandler: (event: 'text-change' | 'selection-change', current: any | Range | null, old: any | Range | null, source: string) => void;
    ngOnDestroy(): void;
    ngOnChanges(changes: SimpleChanges): void;
    addClasses(classList: string): void;
    removeClasses(classList: string): void;
    writeValue(currentValue: any): void;
    setDisabledState(isDisabled?: boolean): void;
    registerOnChange(fn: (modelValue: any) => void): void;
    registerOnTouched(fn: () => void): void;
    registerOnValidatorChange(fn: () => void): void;
    validate(): {
        minLengthError?: {
            given: number;
            minLength: number;
        };
        maxLengthError?: {
            given: number;
            maxLength: number;
        };
        requiredError?: {
            empty: boolean;
        };
    };
    static ɵfac: ɵngcc0.ɵɵFactoryDef<QuillEditorBase, never>;
    static ɵdir: ɵngcc0.ɵɵDirectiveDefWithMeta<QuillEditorBase, never, never, { "required": "required"; "customToolbarPosition": "customToolbarPosition"; "sanitize": "sanitize"; "styles": "styles"; "strict": "strict"; "customOptions": "customOptions"; "customModules": "customModules"; "preserveWhitespace": "preserveWhitespace"; "trimOnValidation": "trimOnValidation"; "valueGetter": "valueGetter"; "valueSetter": "valueSetter"; "format": "format"; "theme": "theme"; "modules": "modules"; "debug": "debug"; "readOnly": "readOnly"; "placeholder": "placeholder"; "maxLength": "maxLength"; "minLength": "minLength"; "formats": "formats"; "scrollingContainer": "scrollingContainer"; "bounds": "bounds"; "trackChanges": "trackChanges"; "classes": "classes"; }, { "onEditorCreated": "onEditorCreated"; "onEditorChanged": "onEditorChanged"; "onContentChanged": "onContentChanged"; "onSelectionChanged": "onSelectionChanged"; "onFocus": "onFocus"; "onBlur": "onBlur"; }, never>;
}

What changes can I make to satisfy this error? I created an issue on the repository but it was dismissed and closed without any help. So that was a dead end.

2

There are 2 answers

0
Paul Strupeikis On

From https://angular.io/guide/updating-to-version-10:

New Breaking Changes

Typescript 3.6, 3.7, and 3.8 are no longer supported. Please update to Typescript 3.9.

To resolve this downgrade from Typescript 4.0.3 to 3.9.7:

npm install [email protected]

I am using Angular 10 and ngx-quill with the same code from ngx-quill-example as you are and this worked for me. This will at least save some time.

1
wizips On

you can disable the check by putting // @ts-nocheck on the line before each line with error