2

不确定标题是否正确地类似于我的问题,但我愿意接受建议。

我有以下方法,它返回 IListItem 数组或扩展该接口的其他接口数组。

import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import { IWebPartContext } from "@microsoft/sp-webpart-base";
import { IListItem} from "./models/IListItem";
import { IFactory } from "./IFactory";
import { INewsListItem } from "./models/INewsListItem";
import { IDirectoryListItem } from "./models/IDirectoryListItem";
import { IAnnouncementListItem } from "./models/IAnnouncementListItem";

export class ListItemFactory implements IFactory {
    private _listItems: IListItem[];
    public getItems(requester: SPHttpClient, siteUrl: string, listName: string): Promise<IListItem[]> {
        switch(listName) {
            case "GenericList":
                let items: IListItem[];
                // tslint:disable-next-line:max-line-length
                return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Author/Title,Editor/Title&$expand=Author,Editor`,
                SPHttpClient.configurations.v1,
                {
                    headers: {
                        "Accept": "application/json;odata=nometadata",
                        "odata-version": ""
                    }
                })
                .then((response: SPHttpClientResponse): Promise<{ value: IListItem[] }> => {
                    return response.json(); 
                })
                .then((json: { value: IListItem[] }) => {
                    console.log(JSON.stringify(json.value));
                    return items=json.value.map((v,i)=>(
                        { 
                            //key: v.id,
                            id: v.Id,
                            title: v.Title,
                            created: v.Created,
                            createdby: v.Author.Title,
                            modified: v.Modified,
                            modifiedby: v.Editor.Title                        
                        }
                    ));
                });  
            case "News":
                let newsitems: INewsListItem[];
                // tslint:disable-next-line:max-line-length
                return requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id,Modified,Created,Created By,Modified By,newsheader,newsbody,expiryDate`,
                SPHttpClient.configurations.v1,
                {
                    headers: {
                        "Accept": "application/json;odata=nometadata",
                        "odata-version": ""
                    }
                })
                .then((response: SPHttpClientResponse): Promise<{ value: INewsListItem[] }> => {
                    return response.json();
                })
                .then((json: { value: INewsListItem[] }) => {
                    return items=json.value.map((v,i)=>(
                        { 
                            id: v.Id,
                            title: v.Title,
                            created: v.Created,
                            createdby: v.Author.Title,
                            modified: v.Modified,
                            modifiedby: v.Editor.Title,
                            newsheader: v.newsheader,
                            newsbody: v.newsbody,
                            expiryDate: v.expiryDate
                        }
                    ));
                });
            case "Announcements":
                let announcementitems: IAnnouncementListItem[];
                return 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: IAnnouncementListItem[] }> => {
                    return response.json();
                })
                .then((json: { value: IAnnouncementListItem[] }) => {
                    return items=json.value.map((v,i)=>(
                        { 
                            id: v.Id,
                            title: v.Title,
                            created: v.Created,
                            createdby: v.Author.Title,
                            modified: v.Modified,
                            modifiedby: v.Editor.Title,
                            announcementBody: v.announcementBody,
                            expiryDate: v.expiryDate
                        }
                    ));
                });
            case "Directory":
                let directoryitems: IDirectoryListItem[];
                return 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: IDirectoryListItem[] }> => {
                    return response.json();
                })
                .then((json: { value: IDirectoryListItem[] }) => {
                    return items=json.value.map((v,i)=>(
                        { 
                            id: v.Id,
                            title: v.Title,
                            created: v.Created,
                            createdby: v.Author.Title,
                            modified: v.Modified,
                            modifiedby: v.Editor.Title,
                            firstName: v.firstName,
                            lastName: v.lastName,
                            mobileNumber: v.mobileNumber,
                            internalNumber: v.internalNumber
                        }
                    ));
                });
            default:
                return null;
        }
      }
}

接口在这里:

export  interface IListItem {
    [key: string]: any;
    id: string;
    title: string;
    modified: Date;
    created: Date;
    modifiedby: string;
    createdby: string;
}




import {IListItem} from "./IListItem";

export interface  INewsListItem extends IListItem {
    newsheader: string;
    newsbody: string;
    expiryDate: Date;
}

import {IListItem} from "./IListItem";

export interface  IAnnouncementListItem extends IListItem {
    announcementBody: string;
    expiryDate: Date;
}


import {IListItem} from "./IListItem";

export interface IDirectoryListItem extends IListItem {
        firstName: string;
        lastName: string;
        mobileNumber: string;
        internalNumber: string;
}

TSX 组件的消费者方法是:

 // read items using factory method pattern and sets state accordingly
  private readItemsAndSetStatus(): void {
    this.setState({
      status: "Loading all items..."
    });

    const factory: ListItemFactory = new ListItemFactory();
    factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName)
    .then((items: IListItem[]) => {
      const keyPart: string = this.props.listName === "GenericList" ? "" : this.props.listName;
        // the explicit specification of the type argument `keyof {}` is bad and
        // it should not be required.
        this.setState<keyof {}>({
          status: `Successfully loaded ${items.length} items`,
          ["Details" + keyPart + "ListItemState"] : {
            items
          },
          columns: buildColumns(items)
        });
    });
  }

我粘贴在整个 TSX 下面仅供参考

//#region Imports
import * as React from "react";
import styles from "./FactoryMethod.module.scss";
import  { IFactoryMethodProps } from "./IFactoryMethodProps";
import {
  IDetailsListItemState,
  IDetailsNewsListItemState,
  IDetailsDirectoryListItemState,
  IDetailsAnnouncementListItemState,
  IFactoryMethodState
} from "./IFactoryMethodState";
import { IListItem } from "./models/IListItem";
import { IAnnouncementListItem } from "./models/IAnnouncementListItem";
import { INewsListItem } from "./models/INewsListItem";
import { IDirectoryListItem } from "./models/IDirectoryListItem";
import { escape } from "@microsoft/sp-lodash-subset";
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import { ListItemFactory} from "./ListItemFactory";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import {
  DetailsList,
  DetailsListLayoutMode,
  Selection,
  buildColumns,
  IColumn
} 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";
import PropTypes from "prop-types";
//#endregion


export default class FactoryMethod extends React.Component<IFactoryMethodProps, IFactoryMethodState> {
  constructor(props: IFactoryMethodProps, state: any) {
    super(props);
    this.setInitialState();
  }

  // lifecycle help here: https://staminaloops.github.io/undefinedisnotafunction/understanding-react/

  //#region Mouting events lifecycle

  // the data returned from render is neither a string nor a DOM node.
  // it's a lightweight description of what the DOM should look like.
  // inspects this.state and this.props and create the markup.
  // when your data changes, the render method is called again.
  // react diff the return value from the previous call to render with
  // the new one, and generate a minimal set of changes to be applied to the DOM.
  public render(): React.ReactElement<IFactoryMethodProps> {
    switch(this.props.listName) {
        case "GenericList":
          // tslint:disable-next-line:max-line-length
          return <this.ListMarqueeSelection items={this.state.DetailsListItemState.items} columns={this.state.columns} />;
        case "News":
          // tslint:disable-next-line:max-line-length
          return <this.ListMarqueeSelection items={this.state.DetailsNewsListItemState.items} columns={this.state.columns}/>;
        case "Announcements":
          // tslint:disable-next-line:max-line-length
          return <this.ListMarqueeSelection items={this.state.DetailsAnnouncementListItemState.items} columns={this.state.columns}/>;
        case "Directory":
          // tslint:disable-next-line:max-line-length
          return <this.ListMarqueeSelection items={this.state.DetailsDirectoryListItemState.items} columns={this.state.columns}/>;
        default:
          return null;
    }
  }

   // invoked once, only on the client (not on the server), immediately AFTER the initial rendering occurs.
   public componentDidMount(): void {
    // you can access any refs to your children
    // (e.g., to access the underlying DOM representation - ReactDOM.findDOMNode).
    // the componentDidMount() method of child components is invoked before that of parent components.
    // if you want to integrate with other JavaScript frameworks,
    // set timers using setTimeout or setInterval,
    // or send AJAX requests, perform those operations in this method.
    this._configureWebPart = this._configureWebPart.bind(this);
    this.readItemsAndSetStatus();
  }

  //#endregion

  //#region Props changes lifecycle events (after a property changes from parent component)
  public componentWillReceiveProps(nextProps: IFactoryMethodProps): void {
    if(nextProps.listName !== this.props.listName) {
      this.readItemsAndSetStatus();
    }
  }

  //#endregion

  //#region private methods
  private _configureWebPart(): void {
    this.props.configureStartCallback();
  }

  public setInitialState(): void {
    this.state = {
      type: "ListItem",
      status: this.listNotConfigured(this.props)
        ? "Please configure list in Web Part properties"
        : "Ready",
      columns:[],
      DetailsListItemState:{
        items:[]
      },
      DetailsNewsListItemState:{
        items:[]
      },
      DetailsDirectoryListItemState:{
        items:[]
      },
      DetailsAnnouncementListItemState:{
        items:[]
      },
    };
  }

  // reusable inline component
  private ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => (
      <div>
          <DetailsList
            items={ itemState.items }
            columns={ itemState.columns }
            setKey="set"
            layoutMode={ DetailsListLayoutMode.fixedColumns }
            selectionPreservedOnEmptyClick={ true }
            compact={ true }>
          </DetailsList>
      </div>
  )

  // read items using factory method pattern and sets state accordingly
  private readItemsAndSetStatus(): void {
    this.setState({
      status: "Loading all items..."
    });

    const factory: ListItemFactory = new ListItemFactory();
    factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName)
    .then((items: IListItem[]) => {
      const keyPart: string = this.props.listName === "GenericList" ? "" : this.props.listName;
        // the explicit specification of the type argument `keyof {}` is bad and
        // it should not be required.
        this.setState<keyof {}>({
          status: `Successfully loaded ${items.length} items`,
          ["Details" + keyPart + "ListItemState"] : {
            items
          },
          columns: buildColumns(items)
        });
    });
  }

  private listNotConfigured(props: IFactoryMethodProps): boolean {
    return props.listName === undefined ||
      props.listName === null ||
      props.listName.length === 0;
  }

  //#endregion
}

问题是它总是返回列表项数组,因此反应组件只显示 IListItem 的列,而不显示其他接口的列

更新1:

尝试在 THEN 中进行强制转换将导致变量 myItems 出现重复警告。

在此处输入图像描述

4

1 回答 1

2

这是一个问题,因为您的 Promise 正在返回 IListItem[]。

如果将工厂中getItems方法中promise的返回类型改为any[]。然后对消费者中getItems方法调用的'then'中的返回类型做同样的事情。然后在消费者中的 listName 上再次切换以转换为所需的类型,它应该可以工作

您可以使用它进行转换,因为您在消费者中有可用的列表名称;

var myItems = null; switch(listName) { case "GenericList": myItems = items as IListItem[]; case "News": myItems = items as INewsListItem[]; case "Announcements": myItems = items as IAnnouncementListItem[]; case "Directory": myItems = items as IDirectoryListItem[]; }

然后在构建列而不是项目时使用 myItems

于 2017-11-16T10:24:40.227 回答