2017-10-09 39 views
1

好吧,這是一個很難寫的問題,讓我看看我能否正確解釋自己。如何正確使用打字稿中的工廠方法

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

但是,您可以創建包含更多列的自定義列表,但它們從基本列表繼承,因此任何新列都將添加到以前的列中。

我的想法是在SharePoint中創建框架與打字稿一個通用的解決方案並作出反應,以便能夠從任何列表讀取和渲染使用Office UI面料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只有一個方法,你可以看到返回列表項

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. 在呈現的方法,該組件接收的項目陣列也是一個列陣列。

  2. 在readItems上,我有一個switch語句,並根據選定的列表名稱,使用不同的工廠並返回適當數組類型的項目。

但是我不知道,如何正確傳遞兩個項目和列參數給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

你有沒有考慮過使用策略設計模式?這將是更靈活的解決方案,海事組織 –

回答

1

很抱歉,但我可能誤解了你的問題;但是如果你想在另一箇中注入一個obj,你爲什麼要擴展這個列表?我會用代碼來解釋:

import {ListItem} from './ListItem'; 
 

 
export class DirectoryListItem { 
 
    constructor(
 
     public listIt: ListItem; 
 
     // instead of repeating code, you inject your base-obj listItem 
 
     public firstName: string, 
 
     public lastName: string, 
 
     public mobileNumber: string, 
 
     public internalNumber: string, 
 
    ) { } 
 
}

也許這不是你問什麼,但如果我理解你的類的問題可能是有用的。 希望這有助於。