[ionic4] unable to assign type "FormControl" to a variable decorated with @input

I am declaring a variable with type “FormControl” as below:

@Input() formControlItem: FormControl;

and when setting the value to it in a method as

this.formController.setValue()

it is throwing error “Cannot read property ‘setValue’ of undefined”… while it was working properly in ionic 3.

Can some one plz help?

@lucasbasquerotto

I see that your input is named formControlItem and you are calling this.formController.

Assuming that’s just a mistake in copying and pasting, even so you are not passing enough information.

If your component has a selector like my-component, make sure the input property is being passed correctly, like:

<my-component ... [formControlItem]="myFormControl">

(and make sure myFormControl is a FormControl and is defined/initialized)

You can put a log before calling setValue() to see if it is defined (if it’s in a ngOnChange() and the component receive several properties, for example, it may be undefined at first, if I’m not mistaken).

console.log('formControlItem', this.formControlItem);

You could log it in ngOnInit() too.

Sorry for the incomplete information. I thought this much info will be enough.

Actually, I am using “rich-text” component in ionic to implement Rich Text Editor. The git url of the component is https://github.com/judgewest2000/Ionic3RichText. I am getting the error rich-text.ts file, you can refer rich-text.ts file here https://github.com/judgewest2000/Ionic3RichText/blob/master/src/components/rich-text/rich-text.ts.

And also pasting that file code below:

import { StatusBar } from '@ionic-native/status-bar';
import { Component, ViewChild, ElementRef, Input, AfterContentInit, OnInit } from '@angular/core';
import { FormControl } from "@angular/forms";


@Component({
	selector: 'rich-text',
	templateUrl: 'rich-text.html',
	styleUrls: ['rich-text.scss']
})
export class RichTextComponent implements AfterContentInit {
	constructor() {
		
	}

	@ViewChild('editor') editor: ElementRef;
	@ViewChild('decorate') decorate: ElementRef;
	@ViewChild('styler') styler: ElementRef;
	@Input() formControlItem: FormControl;
	@Input() placeholderText: string;

	uniqueId = `editor${Math.floor(Math.random() * 1000000)}`;

	private stringTools = {
		isNullOrWhiteSpace: (value: string) => {
			if (value == null || value == undefined) {
				return true;
			}
			value = value.replace(/[\n\r]/g, '');
			value = value.split(' ').join('');

			return value.length === 0;
		}
	};

	getPlaceholderText() {
		if (this.placeholderText !== undefined) {
			return this.placeholderText
		}
		return '';
	}

	
	private updateItem() {
		const element = this.editor.nativeElement as HTMLDivElement;

		if (this.formControlItem != undefined)
			element.innerHTML = this.formControlItem.value;

		// if (element.innerHTML === null || element.innerHTML === '') {
		//   element.innerHTML = '<div></div>';
		// }

		const reactToChangeEvent = () => {

			if (this.stringTools.isNullOrWhiteSpace(element.innerText)) {
				element.innerHTML = '<div></div>';
				if (this.formControlItem != undefined)
					this.formControlItem.setValue(null);
			} else {
				this.formControlItem.setValue(element.innerHTML);
			}
		};

		element.onchange = () => reactToChangeEvent();
		element.onkeyup = () => reactToChangeEvent();
		element.onpaste = () => reactToChangeEvent();
		element.oninput = () => reactToChangeEvent();
	}

	private wireupButtons() {
		let buttons = (this.decorate.nativeElement as HTMLDivElement).getElementsByTagName('button');
		for (let i = 0; i < buttons.length; i++) {
			let button = buttons[i];

			let command = button.getAttribute('data-command');

			if (command.includes('|')) {
				let parameter = command.split('|')[1];
				command = command.split('|')[0];

				button.addEventListener('click', () => {
					document.execCommand(command, false, parameter);
				});
			} else {
				button.addEventListener('click', () => {
					document.execCommand(command);
				});
			}
		}

	}

	// tslint:disable-next-line:use-life-cycle-interface
	ngAfterContentInit() {
		this.updateItem();
		this.wireupButtons();
	}

}

When I interact with the rich-text component, different events (onchange, onkeyup, onpaste, oninput) are called. It is working fine in ionic 3 but in ionic 4, this.formControlItem is not getting initialize and throwing error (as it is undefined). When execution reach to this.formControlItem.setValue(null / element.innerHTML), it throws “cannot read property ‘setValue’ of undefined”.

How are you using the richtext in your HTML (Template)?

Are you passing formControlItem as an input property?

Something like:

my.component.html:

<rich-text [formControlItem]="myFormControlItem"></rich-text>

my.component.ts:

someFunction() {
    this.myFormControlItem = //create the form control
    console.log('myFormControlItem ', this.myFormControlItem)
}

Seeing your code, RichTextComponent expects an input property, so make sure you are passing it properly.

create.html:

<rich-text [formControlItem]="item"></rich-text>

<ion-button expand="full" (click)="clkSubmitListing(item.value)">SAVE</ion-button>

create.ts:

export class create {
    item: FormControl;

	ionViewWillLoad() {
		this.item = this.formBuilder.control('');
	}

    clkSubmitListing (richText) {
    
    }
}

@lucasbasquerotto

image 1A (ionic 3):

image 1B (ionic 3):

In above, both screenshots are when running ionic 3 code. In which, 1A show rich-text.ts debugger on “this.formControllerItem.setValue()” and value of this.formControllerItem at this time can be shown in image 1B.

image 2A (ionic 4):

image 2B (ionic 4):
54%20PM

In above, both screenshots are when running ionic 4 code. In which, 2A show rich-text.ts debugger on “this.formControllerItem.setValue()” and value of this.formControllerItem at this time can be shown in image 2B.

Put a console.log() in ionViewWillLoad, this event is the one that initializes item, and probably isn’t called, because it seems ionViewWillLoad was removed:

In this case, the ionViewWillEnter , ionViewDidEnter , ionViewWillLeave , and ionViewDidLeave have been ported over from V3. Use these events to coordinate actions with Ionic’s own animations system.

Older events like ionViewDidLoad , ionViewCanLeave , and ionViewCanEnter have been removed, and the proper Angular alternatives should be used.

(Although it’s not saying explicitly, I think ionViewWillLoad was one of those that were removed)

You can change the code to:

ngOnInit() {
    this.item = this.formBuilder.control('');
}
1 Like

Yes, it’s working. You always help me. Thousand times thanks my bro.