如何正确使用打字稿中的工厂方法

时间:2017-10-09 13:12:17

标签: reactjs typescript design-patterns sharepoint

嗯,这是一个很难写的问题,让我们看看能不能正确解释自己。

在Sharepoint中,ListItem默认具有属性,id,title,createdby,createddate,modifiedby,modifieddate。

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

我的想法是使用typescript在Sharepoint Framework中创建一个通用解决方案,并做出反应,以便能够从任何列表中读取并使用Office UI Fabric DetailsList组件进行渲染:https://developer.microsoft.com/en-us/fabric#/components/detailslist

所以我开始使用模型:

ListIitem.ts

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);
     } 
}

AnnoucementLIstItem.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;
    }
}

其他工厂看起来也很相似:

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;
    }
}

唯一的区别是,它不是返回ListItem,而是返回DirectoryListItem数组。

直到那里,一切都很清楚,然后我的组件将作为参数之一接收列表名称。

密切关注readItems方法,以及我的问题所关注的渲染方法。

  1. 在render方法中,组件接收一个item数组,但也接收一个列数组。

  2. 在readItems上我有一个switch语句,根据所选列表名称,我使用不同的工厂并返回适当数组类型的项目。

  3. 但是我不确定,如何正确地将项目和列参数传递给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}`);
      }
    }
    

1 个答案:

答案 0 :(得分:1)

很抱歉,但我可能误解了你的问题;但是,如果你想在另一个中注入obj,为什么要扩展列表?我将用代码解释:

&#13;
&#13;
TableColumn
&#13;
&#13;
&#13;

可能这不是你要求的,但如果我理解你的课程问题可能会有用。 希望这有帮助。