问题描述
这是我第一次尝试使用 office ui fabric react JS 在 SharePoint Online 中开发 Web 部件应用程序。对于一个测试(CRUD 应用程序)项目,我开发了一个在线图书库应用程序。我一直在努力从共享点列表中保存/获取日期值,但由于某种原因出现错误,如下所示:
import * as React from 'react';
import styles from './HelloWorld.module.scss';
import { IHelloWorldProps } from './IHelloWorldProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { BookLibListItem } from './BookLibListItem';
import { IBookLibCollection } from './IBookLibCollection';
import { ISPHttpClientOptions,SPHttpClient,SPHttpClientResponse } from '@microsoft/sp-http';
import {
TextField,autobind,PrimaryButton,DetailsList,DetailsListLayoutMode,CheckBoxVisibility,SelectionMode,Dropdown,DatePicker,IDatePickerStrings,IDropdown,IDropdownoption,ITextFieldStyles,IDropdownStyles,DetailsRowCheck,Selection,DayOfWeek
} from 'office-ui-fabric-react';
const DayPickerStrings: IDatePickerStrings = {
months: [
'January','February','march','April','May','June','July','August','September','October','November','December',],shortMonths: ['Jan','Feb','Mar','Apr','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],days: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],shortDays: ['S','M','T','W','F','S'],goToToday: 'Go to today',prevMonthAriaLabel: 'Go to prevIoUs month',nextMonthAriaLabel: 'Go to next month',prevYearariaLabel: 'Go to prevIoUs year',nextYearariaLabel: 'Go to next year',closeButtonAriaLabel: 'Close date picker'
};
/*
export interface IDatePickerBasicExampleState {
firstDayOfWeek?: DayOfWeek;
}
*/
// Configure the columns for the DetailsList component
let _bookListColumns = [
{
key: 'ID',name: 'ID',fieldName: 'ID',minWidth: 50,maxWidth: 100,isResizable: true
},{
key: 'Title',name: 'Title',fieldName: 'Title',{
key: 'Author',name: 'Author',fieldName: 'Author',{
key: 'Publisher',name: 'Publisher',fieldName: 'Publisher',{
key: 'DateOfPublish',name: 'DateOfPublish',fieldName: 'DateOfPublish',{
key: 'ISBN',name: 'ISBN',fieldName: 'ISBN',maxWidth: 150,isResizable: true
}
];
const textFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: { width: 300 } };
const narrowTextFieldStyles: Partial<ITextFieldStyles> = { fieldGroup: { width: 100 } };
const narrowDropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 300 } };
function SetDate(props) {
//const firstDayOfWeek = React.useState(DayOfWeek.Sunday);
return (<DatePicker
{...props.value && moment(props.value).isValid() ? { value: moment(props.value).toDate() } : {}}
className={css(styles.dateFormField,'dateFormField')}
placeholder="Select a date..."
isrequired={props.required}
ariaLabel={props.Title}
parseDateFromString={(dateStr?: string) => { return moment(dateStr,'L').toDate(); }}
formatDate={(date: Date) => (typeof date.toLocaleDateString === 'function') ? date.toLocaleDateString(locale as any) : ""}
strings={DayPickerStrings}
allowTextInput={true}
onSelectDate={(date) => {
if (date) props.valueChanged(date.toISOString());
else props.valueChanged('');
}}
/>);
;
}
function GetDate(props){
const value = (props.value && moment(props.value).isValid()) ? moment(props.value,"YYYY-MM-DD").format('L') : '';
return <FieldDateRenderer text={value} />;
}
export default class HelloWorld extends React.Component<IHelloWorldProps,IBookLibCollection,React.FC> {
private _selection: Selection;
//firstDayOfWeek = React.useState(DayOfWeek.Sunday);
private _onItemsSelectionChanged = () => {
this.setState({
BookListItem: (this._selection.getSelection()[0] as BookLibListItem)
});
}
private _getListItems(): Promise<BookLibListItem[]> {
const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items";
return this.props.context.spHttpClient.get(url,SPHttpClient.configurations.v1)
.then(response => {
console.log(response);
return response.json();
})
.then(json => {
return json.value;
}) as Promise<BookLibListItem[]>;
}
public bindDetailsList(message: string) : void {
this._getListItems().then(listItems => {
console.log(listItems);
this.setState({ BookListItems: listItems,status: message});
});
}
public componentDidMount(): void {
this.bindDetailsList("All Records have been loaded Successfully");
}
@autobind
public btnAdd_click(): void {
const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items";
const spHttpClientOptions: ISPHttpClientOptions = {
"body": JSON.stringify(this.state.BookListItem)
};
this.props.context.spHttpClient.post(url,SPHttpClient.configurations.v1,spHttpClientOptions)
.then((response: SPHttpClientResponse) => {
if (response.status === 201) {
this.bindDetailsList("Record added and All Records were loaded Successfully");
} else {
let errormessage: string = "An error has occured i.e. " + response.status + " - " + response.statusText;
this.setState({status: errormessage});
}
});
}
@autobind
public btnUpdate_click(): void {
let id: number = this.state.BookListItem.Id;
const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items(" + id + ")";
const headers: any = {
"X-HTTP-Method": "MERGE","IF-MATCH": "*",};
const spHttpClientOptions: ISPHttpClientOptions = {
"headers": headers,"body": JSON.stringify(this.state.BookListItem)
};
this.props.context.spHttpClient.post(url,spHttpClientOptions)
.then((response: SPHttpClientResponse) => {
if (response.status === 204) {
this.bindDetailsList("Record Updated and All Records were loaded Successfully");
} else {
let errormessage: string = "An error has occured i.e. " + response.status + " - " + response.statusText;
this.setState({status: errormessage});
}
});
}
@autobind
public btnDelete_click(): void {
let id: number = this.state.BookListItem.Id;
const url: string = this.props.siteUrl + "/_api/web/lists/getbytitle('Book%20Library')/items(" + id + ")";
const headers: any = { "X-HTTP-Method": "DELETE","IF-MATCH": "*" };
const spHttpClientOptions: ISPHttpClientOptions = {
"headers": headers
};
this.props.context.spHttpClient.post(url,spHttpClientOptions)
.then((response: SPHttpClientResponse) => {
if (response.status === 204) {
alert("record got deleted successfully....");
this.bindDetailsList("Record deleted and All Records were loaded Successfully");
} else {
let errormessage: string = "An error has occured i.e. " + response.status + " - " + response.statusText;
this.setState({status: errormessage});
}
});
}
constructor(props: IHelloWorldProps,state: IBookLibCollection){
super(props);
this.state = {
status: 'Ready',BookListItems: [],BookListItem:{
Id: 0,Title: "",Author: "",Publisher: "",DateOfPublish: new Date,ISBN: 0
},};
this._selection = new Selection({
onSelectionChanged: this._onItemsSelectionChanged,});
}
public render(): React.ReactElement<IHelloWorldProps> {
const dropdownRef = React.createRef<IDropdown>();
return (
<div className={ styles.helloWorld }>
<TextField
label="ID"
required={ false }
value={ (this.state.BookListItem.Id).toString()}
styles={textFieldStyles}
onChanged={e => {this.state.BookListItem.Id=e;}}
/>
<TextField
label="Title"
required={ true }
value={ (this.state.BookListItem.Title)}
styles={textFieldStyles}
onChanged={e => {this.state.BookListItem.Title=e;}}
/>
<TextField
label="Author"
required={ true }
value={ (this.state.BookListItem.Author)}
styles={textFieldStyles}
onChanged={e => {this.state.BookListItem.Author=e;}}
/>
<TextField
label="Publisher"
required={ true }
value={ (this.state.BookListItem.Publisher)}
styles={textFieldStyles}
onChanged={e => {this.state.BookListItem.Publisher=e;}}
/>
<SetDate
value={this.state.BookListItem.DateOfPublish}
required={true}
Title="DateOfPublish"
//firstDayOfWeek={firstDayOfWeek}
/>
<TextField
label="ISBN"
required={ true }
value={ (this.state.BookListItem.ISBN.toString())}
styles={textFieldStyles}
onChanged={e => {this.state.BookListItem.ISBN=e;}}
/>
<p className={styles.title}>
<PrimaryButton
text='Add'
title='Add'
onClick={this.btnAdd_click}
/>
<PrimaryButton
text='Update'
onClick={this.btnUpdate_click}
/>
<PrimaryButton
text='Delete'
onClick={this.btnDelete_click}
/>
</p>
<div id="divstatus">
{this.state.status}
</div>
<div>
<DetailsList
items={ this.state.BookListItems}
columns={ _bookListColumns }
setKey='Id'
checkBoxVisibility={ CheckBoxVisibility.onHover}
selectionMode={ SelectionMode.single}
layoutMode={ DetailsListLayoutMode.fixedColumns }
compact={ true }
selection={this._selection}
/>
</div>
</div>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
我使用以下界面作为 SharePoint 中列表的数据模型:
export interface BookLibListItem {
Id: number;
Title: string;
Author: string;
Publisher: string;
DateOfPublish: Date;
ISBN: number;
}
感谢有关此问题的任何建议。
解决方法
Sharepoint 中的日期可能很棘手(尤其是在多个本地化的情况下) - 因此我使用 moment 来格式化日期。对于 DatePicker,我正在使用此代码(带有矩库):
<DatePicker
{...props.value && moment(props.value).isValid() ? { value: moment(props.value).toDate() } : {}}
className={css(styles.dateFormField,'dateFormField')}
placeholder={strings.FormFields.DateFormFieldPlaceholder}
isRequired={props.fieldSchema.Required}
ariaLabel={props.fieldSchema.Title}
parseDateFromString={(dateStr?: string) => { return moment(dateStr,'L').toDate(); }}
formatDate={(date: Date) => (typeof date.toLocaleDateString === 'function') ? date.toLocaleDateString(locale) : ''}
strings={strings.FormFields}
firstDayOfWeek={props.fieldSchema.FirstDayOfWeek}
allowTextInput={true}
onSelectDate={(date) => {
if (date) props.valueChanged(date.toISOString());
else props.valueChanged('');
}}
上面的代码是呈现日期选择器的单独组件。要保存日期,它应该是 ISO 格式(函数 date.toISOString())。
要显示日期,我正在使用 FieldDateRenderer - import { FieldDateRenderer } from "@pnp/spfx-controls-react/lib/FieldDateRenderer";
const value = (props.value && moment(props.value).isValid()) ? moment(props.value,"YYYY-MM-DD").format('L') : '';
return <FieldDateRenderer text={value} />;
,
您如何阅读和写入列表?您是在编写自己的请求,还是在使用 @pnp/sp
(pnpjs) 之类的库?如果您一开始不使用 pnpjs,我会推荐它,请检查 Getting Started Guide。
Fluent UI / office-ui-fabric-react <DatePicker/>
将 Date
对象作为值,并在值更改时输出相同的值。当您从 SharePoint 获取日期时,它可能会表示为 ISO 字符串,您可以使用以下命令将其解析为 Date
对象:
const date = new Date(columnValueFromSharePoint);
为此,您不需要像 moment
这样的额外库。如果您需要进行某种复杂的时区调整或日期操作,我只会在这种情况下推荐 moment
。当您需要做一些简单的事情时,例如添加几分钟、几小时、几天或几个月,您可以使用标准的原始 Date
方法非常简单地完成这些操作。
每当您需要将 Date
写回 SharePoint 时,您都可以将日期输出为 ISO 字符串:
const result = await sp.web.lists.getByTitle('mylist').items.add({
Title: 'New Item',MyDateColumn: date.toISOString()
});
Fluent UI(尚)不包含日期和时间选择器,但 @pnp/spfx-controls-react
项目包含。与 DatePicker 一样,它采用标准 Date
对象作为值并在其 onChange
事件中输出相同的值。这意味着当您需要填充 SharePoint 列时,您可以轻松地将其转换为 ISO 字符串。
有关在 JavaScript 中操作 Date
对象的参考通常可在 MDN Web Docs 上获得