Я создаю плагин для Excel, используя Javascipt API и React.
В настоящее время у меня есть компонент, который по существу создал список текстовых полей, которые отображают данные из самой электронной таблицы. Кроме того, я хотел бы, чтобы они были редактируемыми, а затем могли записывать данные обратно в саму электронную таблицу.
Почти все это уже так или иначе работает, но мне трудно заставить TextFields вести себя так, как я хочу.
По сути, мне нужен список текстовых полей для отображения данных из строки таблицы. Если я выберу другую строку таблицы, я хочу, чтобы значения текстовых полей обновлялись соответствующим образом.
Проблема, с которой я сталкиваюсь, заключается в том, как обеспечить, чтобы текстовые поля обновлялись каждый раз, когда я щелкаю новую строку, и как также убедиться, что они доступны для редактирования.
Если я использую параметр value
с состоянием, то когда я нажимаю на новый элемент в таблице, старое состояние компонента не изменяется при вызове рендеринга и фактически не обновляется ,
Если я использую defaultValue
, он также просто обновляется при первом вызове, а затем никогда.
Функция рендеринга, которая вызывается каждый раз, когда я выбираю новую строку в таблице:
public render() {
const { resource } = this.props;
// Create concept list
const myItems = []
_.forEach(resource, (val) => {
myItems.push(<ListItem resource={val} />)
})
return (
<Stack tokens={{ childrenGap: 3 }}>
{myItems}
</Stack>
);
}
}
ListItem.tsx
import * as React from 'react';
import ReactHtmlParser from 'react-html-parser';
import { TextField, ITextFieldProps, Stack, IconButton, Callout, IStackTokens, } from 'office-ui-fabric-react';
import { getId, IRenderFunction } from 'office-ui-fabric-react/lib/Utilities';
import XbrlTaxonomyElementValue from '../../xbrl/XbrlTaxonomyElementValue';
import { writeValueToCell } from '../Tables';
import getLogger, { REMOTE_LOGGING } from '../../utils/logger';
export interface ListItemState {
isCalloutVisible: boolean;
multiline: boolean;
textFieldValue: string;
}
var con = getLogger(REMOTE_LOGGING);
export interface ListItemProps {
resource: XbrlTaxonomyElementValue;
}
const stackTokens: IStackTokens = {
childrenGap: 10,
maxWidth: 300
};
export default class ListItem extends React.Component<ListItemProps, ListItemState> {
myInput: any;
maxCharWidth: number;
public state: ListItemState = {
isCalloutVisible: false,
multiline: false,
textFieldValue: this.props.resource.value
};
constructor(props: Readonly<ListItemProps>) {
super(props);
this.myInput = React.createRef()
this.maxCharWidth = 35;
}
componentDidMount() { }
private _labelId: string = getId('label');
private _descriptionId: string = getId('description');
private _iconButtonId: string = getId('iconButton');
public render() {
// Controlled Component
const { resource } = this.props;
// if (this.state.textFieldValue != resource.value) {
// this.setState({ textFieldValue: resource.value });
// }
return (
<TextField
componentRef={this.myInput}
required={resource.required}
aria-labelledby={this._labelId}
label={resource.title}
defaultValue={resource.value}
deferredValidationTime={400}
onGetErrorMessage={resource.validator}
description={resource.hint}
onRenderDescription={this._onRenderDescripton}
onRenderLabel={this._onRenderLabel}
multiline={this.state.multiline}
onChange={this._onChange}
onBlur={this._onBlur}
/>
);
}
private _onBlur = (_ev: any) => {
const { resource } = this.props;
writeValueToCell(resource.address.toString(), this.myInput.current.value);
}
private _onChange = (_ev: any, newText: string): void => {
this.setState({ textFieldValue: _ev.target.value })
const newMultiline = newText.length > this.maxCharWidth;
if (newMultiline !== this.state.multiline) {
this.setState({ multiline: newMultiline });
}
};
private _onRenderLabel = (props: ITextFieldProps, defaultRender: IRenderFunction<ITextFieldProps>): JSX.Element => {
/*
We are using the TextField description as the callout text. If we don't provide any text, then
don't render the button, just render a default label.
*/
if (!props.description)
return (defaultRender(props))
return (
<>
<Stack horizontal verticalAlign="center">
<span id={this._labelId}>{defaultRender(props)}</span>
<IconButton
id={this._iconButtonId}
iconProps={{ iconName: 'Info' }}
title="Info"
ariaLabel="Info"
onClick={this._onIconClick}
styles={{ root: { marginBottom: -3 } }}
tabIndex={-1} // Skip button when hitting the tab key
/>
</Stack>
{this.state.isCalloutVisible && (
<Callout
target={'#' + this._iconButtonId}
setInitialFocus={true}
onDismiss={this._onDismiss}
ariaDescribedBy={this._descriptionId}
role="alertdialog"
>
<Stack tokens={stackTokens} horizontalAlign="start" styles={{ root: { padding: 8 } }}>
<span id={this._descriptionId}>{ReactHtmlParser(props.description)}</span>
{/* <DefaultButton onClick={this._onDismiss}>Close</DefaultButton> */}
</Stack>
</Callout>
)}
</>
);
};
private _onRenderDescripton = (): JSX.Element => {
return <div></div> // Return nothing
}
private _onIconClick = (): void => {
this.setState({ isCalloutVisible: !this.state.isCalloutVisible });
};
private _onDismiss = (): void => {
this.setState({ isCalloutVisible: false });
};
}
Я знаю, что это не так Это новая проблема, однако я пока не смог решить ее обычными методами и надеялся, что кто-то может указать мне правильное направление.