I am creating a custom code control using the react framework. It is a text input that should return a text as output after the user entered text in the control.
Now I have the following manifest (shorted):
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<property name="Text"
display-name-key="Text"
description-key="Text of the text input"
of-type="SingleLine.Text"
usage="output"
required="true" />
<resources>
...
</resources>
</control>
</manifest>
Then I have the following index.ts:
imports ...
export class CustomTextInput implements ComponentFramework.ReactControl<IInputs, IOutputs> {
private notifyOutputChanged: () => void;
private _text: string | undefined;
private _props: ITextInputProps;
constructor() { }
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
void {
this.notifyOutputChanged = notifyOutputChanged;
this._props = {
// ... some properties
default: context.parameters.Default.raw!,
maxLength: context.parameters.MaxLength.raw!,
onTextChange: this.onTextChange,
}
console.log("init calls this.onTextChange: " + JSON.stringify(this._props.default))
this.onTextChange(this._props.default);
}
public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {
console.log("update view");
if (context.updatedProperties.indexOf('Default') > -1 || this._props.default !== context.parameters.Default.raw!) {
this._props.default = context.parameters.Default.raw!;
}
if (context.updatedProperties.indexOf('MaxLength') > -1 || this._props.maxLength !== context.parameters.MaxLength.raw!) {
const newMaxLength = this._props.maxLength = context.parameters.MaxLength.raw!;
if (this._text && this._text.length > newMaxLength) {
this._text = this._text.substring(0, newMaxLength) || "";
}
}
return React.createElement(
TextInput, this._props
);
}
public getOutputs(): IOutputs {
console.log("getOutputs, this._text: " + JSON.stringify(this._text));
return { Text: this._text } as IOutputs;
}
onTextChange = (newValue: string | undefined): void => {
console.log("onTextChange");
console.log("newValue: " + JSON.stringify(newValue));
if (newValue && newValue.trim().length !== 0) {
this._text = newValue;
} else {
this._text = undefined;
}
console.log(this._text);
this.notifyOutputChanged();
};
My React Component file TextInput.tsx looks like this:
imports ...
export interface ITextInputProps {
default?: string;
maxLength?: number;
onTextChange: (newText: string) => void
}
export interface ITextInputState {
text: string;
}
export class TextInput extends React.Component<ITextInputProps, ITextInputState> {
constructor(props: ITextInputProps) {
super(props);
this.state = {
text: props.default || ""
};
}
public render(): React.ReactNode {
return (
<ThemeProvider theme={this.props.theme}>
<TextInputContainer>
<LabelsContainer>
<Label>{this.props.label}</Label>
{this.props.hasClearButton && <ClearLabel onClick={this.handleOnClear}>Clear</ClearLabel>}
</LabelsContainer>
<Input placeholder={this.props.hintText} value={this.state.text.trim() !== "" ? this.state.text : ""} spellCheck={this.props.shouldSpellCheck}
onChange={this.handleInputChange} type={this.props.format === "Text" ? "text" : "number"}
maxLength={this.props.maxLength} />
</TextInputContainer>
</ThemeProvider>
)
}
componentDidUpdate(prevProps: ITextInputProps) {
if (prevProps.maxLength !== this.props.maxLength || this.props.maxLength && this.props.maxLength < this.state.text.length) {
const { maxLength, onTextChange } = this.props;
let { text } = this.state;
if (maxLength === undefined || maxLength === null) {
text = "";
} else if (maxLength && text.length > maxLength) {
text = text.substring(0, maxLength);
}
this.setState({ text }, () => onTextChange(text));
}
}
handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
console.log("handleInputChange: newValue: " + JSON.stringify(newValue));
this.setState({
text: newValue
});
this.props.onTextChange(newValue);
}
handleOnClear = () => {
console.log("handleOnClear")
console.log("this.state before: "+ JSON.stringify(this.state));
this.setState({
text: ""
}, () => console.log("this.state after: " + JSON.stringify(this.state)));
this.props.onTextChange("");
}
}
When I add the code control to an app and I add a normal powerapps textlabel it looks like this:

With TextLabel.Text being: "CustomTextInput1.Text: " & CustomTextInput1.Text
Now when I type text in the control it updates the label as well. Only if I try to delete all characters, it will stick with the old value unless I save and refresh the page.
Same thing when I hit the clear button: It deletes the content in the control, but doesn't update the value of CustomTextInput1.Text in the TextLabel. It looks like this (note the logs on the right).

Does anyone know why it would not update the TextProperty although getOutputs is called with the undefined value?
Also note that in the test harness the output value also shows the value of undefined after activating the clear or deleting the letters. And if I delete the letters one by one in the power app it updates the label each time except for the last letter (control input is empty but CustomTextInput1.Text shows the last letter).