I am using a decorator to mark a property case type and when a value changes for one of the properties marked as 'upper' or 'lower' and the new value is not case typed correctly, I change the case and then move the cursor on the correct position becasue changing the FormControl's value will put the cursor at the end of the value.
This works for typing, deleting or pasting text no matter the position or the case type.
Maybe it could be transformed into a directive to which will be passed the case type for the input for a more elegant solution.
The folowing code will check every FormControl that is binded to a property marked as 'upper' or 'lower':
import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core';
import { takeUntil, filter } from 'rxjs/operators';
----
constructor(private _renderer2: Renderer2){}
ngOnInit() {
this.getProperties(this.entity)
.filter(prop => new Array<string>('upper', 'lower')
.includes(this.getPropertyCaseType(this.entity, prop)))
.forEach(prop => {
this.form.controls[prop].valueChanges
.pipe(filter(v => {
if (!v) return false;
let caseType = this.getPropertyCaseType(this.entity, prop);
if (caseType === 'upper') return v !== (<string>v).toUpperCase();
if (caseType === 'lower') return v !== (<string>v).toLowerCase();
return false;
}))
.subscribe(t => {
let oldVal = this.form.value[prop];
let currentVal = <string>t;
this.form.controls[prop].setValue(this.getPropertyCaseType(this.entity, prop) === 'upper' ?
currentVal.toUpperCase() :
currentVal.toLowerCase());
let inputElement = this._renderer2.selectRootElement(`#input_${prop}`);
if (!inputElement || !oldVal) return;
let diff: string = '';
for (let i = 0; i < currentVal.length; i++)
if (currentVal[i] !== oldVal[i - diff.length])
diff = `${diff}${currentVal[i]}`;
let diffPosition = currentVal.lastIndexOf(diff);
inputElement.setSelectionRange(diffPosition + diff.length, diffPosition + diff.length);
});
});
}