Hi experts: When I customize a List View Extension – Command Bar on SharePoint 2019 On-Premises and click on my custom Command Bar, my BaseDialog – Panel appears. I have successfully retrieved the options from a Choice field in the list and displayed them in the DropDown List. Unfortunately, when I select a text, I am unable to retrieve the selected text.
I have also attached a link to an image related to the issue. As follows:
enter image description here
Here is my entire code. I have spent a long time but still cannot find the issue. I wonder if anyone with experience can provide some directions or a solution to help me resolve this problem.
I really appreciate everyone’s help. Thanks again!
A. Explanation:
(1) At the start of the program execution, the onOpen() function is called to retrieve the options from the backend Choice field in the specific list and then insert the extracted options into the DropDown List.
componentDidMount() {
console.log('componentDidMount has been called');
if (this.props.onOpen) {
this.props.onOpen();
}
}
public onOpen(): void {
console.log('onOpen() ==> I am here.');
this.loadChoiceFieldOptions(); // Load machine options
}
private async loadChoiceFieldOptions(): Promise<void> {
console.log('loadChoiceFieldOptions() ==> I am here.');
try {
const listId = '5dadf73c-117d-44ea-a755-23cb4786ea95'; // 換成你的清單 GUID
const fieldInternalName = '_x6a5f__x53f0__x7de8__x865f_'; // <-- 換成你的欄位 Internal Name
const field = await sp.web.lists.getById(listId)
.fields.getByInternalNameOrTitle(fieldInternalName)
.get();
const choices: string[] = (field as any).Choices; // ← 這樣就不報錯了
this.machineOptions = choices.map(choice => ({
key: choice,
text: choice
}));
this.render(); // Re-render to update the machine options
} catch (error) {
console.error('載入機台選項錯誤:', error);
this.machineOptions = [];
this.render(); // Re-render to update the message
}
}
(2) In the render function, I generate a DropDown List.
<Dropdown
label="機台編號"
options={[defaultOption, ...this.filterMachineOptions()]}
onSelect={(
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption
) => {
console.log('Dropdown onChange fired!', option);
}}
selectedKey={this.state.machineId || 'placeholder'}
/>
(3) However, in the RdasSearchPanel class, there is another render process (I am not sure if this is incorrect)
export default class RdasSearchPanel extends BaseDialog {
private machineOptions: IDropdownOption[] = [];
public render(): void {
ReactDOM.render(
<RdasSearchPanelContent
onDismiss={() => this.close()}
onOpen={() => this.onOpen()}
machineOptions={this.machineOptions}
/>,
this.domElement
);
}
B. Here is my entire code.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { BaseDialog } from '@microsoft/sp-dialog';
import { Panel, PanelType, TextField, PrimaryButton, MessageBar, MessageBarType, Dropdown, IDropdownOption } from 'office-ui-fabric-react';
import { sp } from '@pnp/sp/presets/all';
export interface IRdasSearchPanelState {
projectCode: string;
lotNumber: string;
machineId: string;
items: any[];
message: string;
machineOptions: IDropdownOption[];
searchQuery: string;
}
class RdasSearchPanelContent extends React.Component<any, IRdasSearchPanelState> {
constructor(props: any) {
super(props);
this.state = {
projectCode: '',
lotNumber: '',
machineId: '',
items: [],
message: '',
machineOptions: props.machineOptions || [],
searchQuery: ''
};
}
componentDidMount() {
console.log('componentDidMount has been called');
if (this.props.onOpen) {
this.props.onOpen();
}
}
componentDidUpdate(prevProps: any) {
if (prevProps.machineOptions !== this.props.machineOptions) {
this.setState({ machineOptions: this.props.machineOptions });
}
}
handleInputChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
const target = event.target as HTMLInputElement;
const name = target.name;
const value = target.value;
this.setState({ [name]: value } as unknown as Pick<IRdasSearchPanelState, keyof IRdasSearchPanelState>);
};
handleDropdownChange = (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption): void => {
console.log('handleDropdownChange ==> I am here.');
if (option && option.key !== 'placeholder') {
this.setState({ machineId: option.key as string });
}
};
filterMachineOptions = (): IDropdownOption[] => {
return this.state.machineOptions.filter(option => option.text.toLowerCase().includes(this.state.searchQuery.toLowerCase()));
};
searchListItems = async (): Promise<void> => {
try {
const { projectCode, lotNumber, machineId } = this.state;
const baseUrl = window.location.origin;
const url = `${baseUrl}/Lists/List4/AllItems.aspx?useFiltersInViewXml=1&FilterField1=LinkTitle&FilterValue1=${projectCode}&FilterField2=_x6279__x865f_&FilterValue2=${lotNumber}&FilterField3=_x6a5f__x53f0__x7de8__x865f_&FilterValue3=${machineId}`;
window.location.href = url;
} catch (error) {
console.error('Error searching list items: ', error);
this.setState({
message: 'Error searching list items'
});
}
};
render() {
const defaultOption: IDropdownOption = { key: 'placeholder', text: '請選擇機台' };
return (
<Panel
isLightDismiss={false}
isOpen={true}
type={PanelType.medium}
onDismissed={this.props.onDismiss}
>
<h2>Hello there from my custom Panel</h2>
<TextField
label="提案序號"
name="projectCode"
onKeyUp={this.handleInputChange}
/>
<TextField
label="批號"
name="lotNumber"
onKeyUp={this.handleInputChange}
/>
<TextField
label="Search Machine"
onKeyUp={(event) => this.setState({ searchQuery: (event.target as HTMLInputElement).value })}
/>
<Dropdown
label="機台編號"
options={[defaultOption, ...this.filterMachineOptions()]}
onSelect={(
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption
) => {
console.log('Dropdown onChange fired!', option);
}}
selectedKey={this.state.machineId || 'placeholder'}
/>
<PrimaryButton
text='Search'
onClick={this.searchListItems}
/>
{this.state.message &&
<MessageBar
messageBarType={MessageBarType.info}
isMultiline={false}
>
{this.state.message}
</MessageBar>
}
{this.state.items.length > 0 &&
<ul>
{this.state.items.map(item => (
<li key={item.Id}>{item.Title}</li>
))}
</ul>
}
<div>
<p>Project Code: {this.state.projectCode}</p>
<p>Lot Number: {this.state.lotNumber}</p>
<p>Machine ID: {this.state.machineId}</p>
</div>
</Panel>
);
}
}
export default class RdasSearchPanel extends BaseDialog {
private machineOptions: IDropdownOption[] = [];
public render(): void {
ReactDOM.render(
<RdasSearchPanelContent
onDismiss={() => this.close()}
onOpen={() => this.onOpen()}
machineOptions={this.machineOptions}
/>,
this.domElement
);
}
public onOpen(): void {
console.log('onOpen() ==> I am here.');
this.loadChoiceFieldOptions(); // Load machine options
}
private async loadChoiceFieldOptions(): Promise<void> {
console.log('loadChoiceFieldOptions() ==> I am here.');
try {
const listId = '5dadf73c-117d-44ea-a755-23cb4786ea95'; // 換成你的清單 GUID
const fieldInternalName = '_x6a5f__x53f0__x7de8__x865f_'; // <-- 換成你的欄位 Internal Name
const field = await sp.web.lists.getById(listId)
.fields.getByInternalNameOrTitle(fieldInternalName)
.get();
const choices: string[] = (field as any).Choices; // ← 這樣就不報錯了
this.machineOptions = choices.map(choice => ({
key: choice,
text: choice
}));
this.render(); // Re-render to update the machine options
} catch (error) {
console.error('載入機台選項錯誤:', error);
this.machineOptions = [];
this.render(); // Re-render to update the message
}
}
}