Reactjs 如何正确使用typescript中的工厂方法

Reactjs 如何正确使用typescript中的工厂方法,reactjs,typescript,design-patterns,sharepoint,Reactjs,Typescript,Design Patterns,Sharepoint,好吧,这是一个很难回答的问题,让我们看看我是否能正确地解释我自己 在Sharepoint中,ListItem默认具有以下属性:id、标题、createdby、createddate、modifiedby、modifieddate 但是您可以创建包含更多列的自定义列表,但它们继承自基本列表,因此任何新列都将添加到以前的列中 我的想法是使用typescript在Sharepoint Framework中创建一个通用解决方案,并能够使用Office UI Fabric Details列表组件从任何列表

好吧,这是一个很难回答的问题,让我们看看我是否能正确地解释我自己

在Sharepoint中,ListItem默认具有以下属性:id、标题、createdby、createddate、modifiedby、modifieddate

但是您可以创建包含更多列的自定义列表,但它们继承自基本列表,因此任何新列都将添加到以前的列中

我的想法是使用typescript在Sharepoint Framework中创建一个通用解决方案,并能够使用Office UI Fabric Details列表组件从任何列表中读取和呈现:

所以我从模型开始:

列表项

export  class ListItem {
    constructor(
        public id: string,
        public title: string,
        public modified: Date,
        public created: Date,
        public modifiedby: string,
        public createdby: string,
    ) { }
}
DirectoryListItem.ts

import {ListItem} from './ListItem';

export class DirectoryListItem extends ListItem {
    constructor(
        public id: string,
        public title: string,
        public modified: Date,
        public created: Date,
        public modifiedby: string,
        public createdby: string,
        public firstName: string,
        public lastName: string,
        public mobileNumber: string,
        public internalNumber: string,
    ) {
        super(id, title, modified, created, modifiedby, createdby);
     } 
}
AnnouListItem.ts

import {ListItem} from './ListItem';

export class  AnnouncementListItem extends ListItem {
    constructor(
        public id: string,
        public title: string,
        public modified: Date,
        public created: Date,
        public modifiedby: string,
        public createdby: string,
        public announcementBody: string,
        public expiryDate: Date
    ) {
        super(id, title, modified, created, modifiedby, createdby);
     }   
}
等等

然后,我创建了一个只包含一个方法的ListItemFactory,您可以看到它返回一个ListItem数组

import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import { IWebPartContext } from '@microsoft/sp-webpart-base';
import {ListItem} from './models/ListItem';

export class ListItemFactory{    
    public _getItems(requester: SPHttpClient, siteUrl: string, listName: string): ListItem[] {
        let items: ListItem[];
        requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
        SPHttpClient.configurations.v1,
        {
            headers: {
            'Accept': 'application/json;odata=nometadata',
            'odata-version': ''
            }
        })
        .then((response: SPHttpClientResponse): Promise<{ value: ListItem[] }> => {
            return response.json();
        })
        .then((response: { value: ListItem[] }): void => {
            items= response.value;
        });
        return items;
    }
}
从'@microsoft/sp http'导入{SPHttpClient,SPHttpClientResponse};
从'@microsoft/sp webpart base'导入{IWebPartContext};
从“./models/ListItem”导入{ListItem};
导出类ListItemFactory{
public _getItems(请求者:SPHttpClient,siteUrl:string,listName:string):ListItem[]{
let items:ListItem[];
requester.get(`${siteUrl}/\u-api/web/lists/getbytitle(`${listName}'))/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
标题:{
“接受”:“application/json;odata=nometadata”,
“odata版本”:”
}
})
.然后((回复:SPHttpClientResponse):Promise=>{
返回response.json();
})
.then((响应:{value:ListItem[]}):void=>{
项目=响应值;
});
退货项目;
}
}
其他工厂看起来也很相似:

import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import {ListItemFactory} from './ListItemFactory';
import {ListItem} from './models/ListItem';
import {DirectoryListItem} from './models/DirectoryListItem';

export class DirectoryListItemFactory extends ListItemFactory {
    public _getItems(requester: SPHttpClient, siteUrl: string, listName: string): DirectoryListItem[] {
        let items: DirectoryListItem[];
        requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
        SPHttpClient.configurations.v1,
        {
            headers: {
            'Accept': 'application/json;odata=nometadata',
            'odata-version': ''
            }
        })
        .then((response: SPHttpClientResponse): Promise<{ value: DirectoryListItem[] }> => {
            return response.json();
        })
        .then((response: { value: DirectoryListItem[] }): void => {
            items= response.value;
        });
        return items;
    }
}
从'@microsoft/sp http'导入{SPHttpClient,SPHttpClientResponse};
从“/ListItemFactory”导入{ListItemFactory};
从“./models/ListItem”导入{ListItem};
从“./models/DirectoryListItem”导入{DirectoryListItem};
导出类DirectoryListItemFactory扩展ListItemFactory{
public _getItems(请求者:SPHttpClient,siteUrl:string,listName:string):DirectoryListItem[]{
let items:DirectoryListItem[];
requester.get(`${siteUrl}/\u-api/web/lists/getbytitle(`${listName}'))/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
标题:{
“接受”:“application/json;odata=nometadata”,
“odata版本”:”
}
})
.然后((回复:SPHttpClientResponse):Promise=>{
返回response.json();
})
.then((响应:{value:DirectoryListItem[]}):void=>{
项目=响应值;
});
退货项目;
}
}
唯一的区别是,它不返回ListItem,而是返回DirectoryListItem数组

在那之前,一切对我来说都很清楚,然后我有了我的组件,它将作为参数之一接收,列表名

请密切注意readItems方法和render方法,我的问题集中在这里

  • 在render方法中,组件接收一个项目数组,但也接收一个列数组

  • 在readItems上,我有一个switch语句,根据所选的列表名,我使用不同的工厂并返回相应数组类型上的项

  • 但是,我不确定如何正确地将items和columns参数传递给DetailList组件,以使此解决方案尽可能通用

    import * as React from 'react';
    import styles from './FactoryMethod.module.scss';
    import { IFactoryMethodProps } from './IFactoryMethodProps';
    import { IFactoryMethodCrudState } from './IFactoryMethodCrudState';
    import { ListItem } from './models/ListItem';
    import { escape } from '@microsoft/sp-lodash-subset';
    import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
    import { ListItemFactory} from './ListItemFactory';
    import { AnnouncementListItemFactory} from './AnnouncementListItemFactory';
    import { DirectoryListItemFactory} from './DirectoryListItemFactory';
    import { NewsListItemFactory} from './NewsListItemFactory';
    import { TextField } from 'office-ui-fabric-react/lib/TextField';
    import {
      DetailsList,
      DetailsListLayoutMode,
      Selection
    } from 'office-ui-fabric-react/lib/DetailsList';
    import { MarqueeSelection } from 'office-ui-fabric-react/lib/MarqueeSelection';
    import { autobind } from 'office-ui-fabric-react/lib/Utilities';
    
    let _items: any[];
    
    let _columns = [
      {
        key: 'column1',
        name: 'Name',
        fieldName: 'name',
        minWidth: 100,
        maxWidth: 200,
        isResizable: true
      },
      {
        key: 'column2',
        name: 'Value',
        fieldName: 'value',
        minWidth: 100,
        maxWidth: 200,
        isResizable: true
      },
    ];
    
    export default class FactoryMethod extends React.Component<any, any> {
      private listItemEntityTypeName: string = undefined;
      private _selection: Selection;
    
      constructor(props: IFactoryMethodProps, state: IFactoryMethodCrudState) {
        super(props);
    
        /* this.state = {
          status: this.listNotConfigured(this.props) ? 'Please configure list in Web Part properties' : 'Ready',
          items: []
        }; */
    
        this._selection = new Selection({
          onSelectionChanged: () => this.setState({ selectionDetails: this._getSelectionDetails() })
        });
    
        this.state = {
          status: this.listNotConfigured(this.props) ? 'Please configure list in Web Part properties' : 'Ready',
          items: _items,
          selectionDetails: this._getSelectionDetails()
        };
      }
    
    
    
      public componentWillReceiveProps(nextProps: IFactoryMethodProps): void {
        this.listItemEntityTypeName = undefined;
        this.setState({
          status: this.listNotConfigured(nextProps) ? 'Please configure list in Web Part properties' : 'Ready',
          items: []
        });
      }
    
      public render(): React.ReactElement<IFactoryMethodProps> {
        let { items, selectionDetails } = this.state;
    
            return (
              <div>
                <div>{ selectionDetails }</div>
                <TextField
                  label='Filter by name:'
                  onChanged={ this._onChanged }
                />
                <MarqueeSelection selection={ this._selection }>
                  <DetailsList
                    items={ items }
                    columns={ _columns }
                    setKey='set'
                    layoutMode={ DetailsListLayoutMode.fixedColumns }
                    selection={ this._selection }
                    selectionPreservedOnEmptyClick={ true }
                    onItemInvoked={ this._onItemInvoked }
                    compact={ true }
                  />
                </MarqueeSelection>
              </div>
            );
    
    
      }
    
      private readItems(): void {
        this.setState({
          status: 'Loading all items...',
          items: []
        });
    
        //Here its where we actually use the pattern to make our coding easier.
        switch(this.props.listName)
        {
          case "List":
            let factory = new  ListItemFactory();
            let listItems  = factory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);  
            this.setState({
              status: `Successfully loaded ${listItems.length} items`,
              items: listItems
            });      
            break;
          case "Announcements":
            let announcementFactory = new  AnnouncementListItemFactory();
            let announcementlistItems  = announcementFactory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);  
            this.setState({
              status: `Successfully loaded ${listItems.length} items`,
              items: announcementlistItems
            });         
            break;
          case "News":
            let newsFactory = new  NewsListItemFactory();
            let newsListItems  = newsFactory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);  
            this.setState({
              status: `Successfully loaded ${listItems.length} items`,
              items: newsListItems
            });  
            break;
          case "Directory":
            let directoryFactory = new  DirectoryListItemFactory();
            let directoryListItems  = directoryFactory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);  
            this.setState({
              status: `Successfully loaded ${listItems.length} items`,
              items: directoryListItems
            });  
            break;
          default : 
            break;
        }    
      }
    
      private _getSelectionDetails(): string {
        let selectionCount = this._selection.getSelectedCount();
    
        switch (selectionCount) {
          case 0:
            return 'No items selected';
          case 1:
            return '1 item selected: ' + (this._selection.getSelection()[0] as any).name;
          default:
            return `${selectionCount} items selected`;
        }
      }
    
      private listNotConfigured(props: IFactoryMethodProps): boolean {
        return props.listName === undefined ||
          props.listName === null ||
          props.listName.length === 0;
      }
    
      @autobind
      private _onChanged(text: any): void {
        this.setState({ items: text ? _items.filter(i => i.name.toLowerCase().indexOf(text) > -1) : _items });
      }
    
      private _onItemInvoked(item: any): void {
        alert(`Item invoked: ${item.name}`);
      }
    }
    
    import*as React from'React';
    从“./FactoryMethod.module.scss”导入样式;
    从“/IFactoryMethodOps”导入{IFactoryMethodOps};
    从“/IFactoryMethodCrudState”导入{IFactoryMethodCrudState};
    从“./models/ListItem”导入{ListItem};
    从'@microsoft/sp lodash subset'导入{escape};
    从“@microsoft/sp http”导入{SPHttpClient,SPHttpClientResponse};
    从“/ListItemFactory”导入{ListItemFactory};
    从“/AnnouncementListItemFactory”导入{AnnouncementListItemFactory};
    从“./DirectoryListItemFactory”导入{DirectoryListItemFactory};
    从“./NewsListItemFactory”导入{NewsListItemFactory};
    从“office ui fabric react/lib/TextField”导入{TextField};
    进口{
    详细列表,
    DetailsListLayoutMode,
    挑选
    }来自“office ui fabric react/lib/DetailsList”;
    从“office ui fabric react/lib/MarqueeSelection”导入{MarqueeSelection};
    从“office ui fabric react/lib/Utilities”导入{autobind};
    let_项目:任何[];
    设_列=[
    {
    键:“column1”,
    姓名:'姓名',
    fieldName:'名称',
    最小宽度:100,
    最大宽度:200,
    isResizable:对
    },
    {
    键:“column2”,
    名称:“值”,
    字段名:“值”,
    最小宽度:100,
    最大宽度:200,
    isResizable:对
    },
    ];
    导出默认类FactoryMethod扩展React.Component{
    私有listItemEntityTypeName:string=未定义;
    私人选择:选择;
    构造函数(道具:IFactoryMethodOps,状态:IFactoryMethodCrudState){
    超级(道具);
    /*此.state={
    状态:this.listNotConfigured(this.props)“‘请在Web部件属性中配置列表’:‘就绪’,
    项目:[]
    }; */
    此。_选择=新选择({
    onSelectionChanged:()=>this.setState({selectionDetails:this.\u getSelectionDetails()})
    });
    此.state={
    状态:this.listNotConfigured(this.props)“‘请在Web部件属性中配置列表’:‘就绪’,
    项目:_项目,
    selectionDetails:这是。\ u getSelectionDetails()
    };
    }
    公共组件将接收道具(nextrops:ifactorymethodrops):无效{
    this.listItemEntityTypeName=未定义;
    这是我的国家({
    状态:this.listNotConfigured(nextrops)“‘请在Web部件属性中配置列表’:‘就绪’,
    项目:[]
    });
    }
    public render():React.ReactElement{
    让{items,selectionDetails}=this.state;
    返回(
    {selectionDetails