質問

I'm new to SPFX / React Development, So I thought I'd start with a simple project!

I want to add a Resource Quick Access webpart on my Modern Sharepoint Page where users can quickly access SharePoint/OneDrive/Team files etc... I am using the MSGraphClient with UI Fabric

I'm getting this error

(property) BaseWebPart.properties: AlphaResources This property is the pointer to the custom property bag of the web part. @readonly No overload matches this call. The last overload gave the following error. Argument of type '{ description: any; context: WebPartContext; }' is not assignable to parameter of type 'Attributes'. Object literal may only specify known properties, and 'description' does not exist in type 'Attributes'.ts(2769) index.d.ts(238, 14): The last overload is declared here.

I really don't understand why, I haven't had this in previous test web parts

So I could really do with your help.

in the webpart.ts I have

import * as React from 'react';
import * as ReactDom from 'react-dom';

import { Version } from '@microsoft/sp-core-library';
import {
        IPropertyPaneConfiguration,
        PropertyPaneTextField,
        PropertyPaneLabel,
        PropertyPaneToggle,
        PropertyPaneDropdown,
        PropertyPaneCheckbox,
        PropertyPaneLink,
        PropertyPaneSlider
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';

import * as strings from 'AlphaResourcesWebPartStrings';
import AlphaResources  from './components/AlphaResources';
import IAlphaResourcesProps from './components/AlphaResources';


export default class AlphaResourcesWebPart extends BaseClientSideWebPart<IAlphaResourcesProps> {

public render(): void {
 const element: React.ReactElement<IAlphaResourcesProps> = React.createElement(
  AlphaResources,
  {
    description: this.properties.description, 
    context: this.context 
  }
);

ReactDom.render(element, this.domElement);
}

protected onDispose(): void {
     ReactDom.unmountComponentAtNode(this.domElement);
}

protected get dataVersion(): Version {
  return Version.parse('1.0');
}

protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
  return {
    pages: [
      {
        header: {
          description: strings.PropertyPaneDescription
        },
       groups: [
         {
           groupName: strings.BasicGroupName,
           groupFields: [
             PropertyPaneTextField('description', {
               label: strings.DescriptionFieldLabel
             })
           ]
         }
       ]
     }
   ]
 };
}
}

in the AlphaResources.tsx (code based on example [https://developer.microsoft.com/en-us/fabric#/controls/web/detailslist][1])

import * as React from 'react';
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { MSGraphClient } from '@microsoft/sp-http';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { Fabric } from 'office-ui-fabric-react/lib/Fabric';
import { Announced } from 'office-ui-fabric-react/lib/Announced';
import { DetailsList, DetailsListLayoutMode, Selection, SelectionMode, IColumn } from 'office-ui-fabric-react/lib/DetailsList';
import { mergeStyleSets } from 'office-ui-fabric-react/lib/Styling';
import { Pivot, PivotItem } from 'office-ui-fabric-react/lib/Pivot';
import { Nav, INavLink } from 'office-ui-fabric-react/lib/Nav';
import { Link } from 'office-ui-fabric-react/lib/Link';

const classNames = mergeStyleSets({
    fileIconHeaderIcon: {
    padding: 0,
    fontSize: '16px'
},
fileIconCell: {
    textAlign: 'center',
    selectors: {
    '&:before': {
    content: '.',
    display: 'inline-block',
    verticalAlign: 'middle',
    height: '100%',
    width: '0px',
    visibility: 'hidden'
}
}
},
fileIconImg: {
  verticalAlign: 'middle',
  maxHeight: '16px',
  maxWidth: '16px'
},
  controlWrapper: {
    display: 'flex',
    flexWrap: 'wrap'
  },
  exampleToggle: {
    display: 'inline-block',
    marginBottom: '10px',
    marginRight: '30px'
  },
  selectionDetails: {
    marginBottom: '20px'
  }
});
const controlStyles = {
  root: {
    margin: '0 30px 20px 0',
    maxWidth: '300px'
  }
};

export interface IAlphaResourcesProps {
  description: string;
  context: WebPartContext;
}

export interface IAlphaResourcesState {
  columns: IColumn[];
  items: IDocument[];
  selectionDetails: string;
  isModalSelection: boolean;
  isCompactMode: boolean;
  announcedMessage?: string;
}

export interface IDocument {
  key: string;
  name: string;
  value: string;
  iconName: string;
  webUrl:string;
  fileType: string;
  modifiedBy: string;
  dateModified: string;
  dateModifiedValue: number;
  fileSize: string;
  fileSizeRaw: number;
  contentType:string;
  parentId:string;
  parentDriveId:string;
}


export default class AlphaResources extends React.Component<{}, IAlphaResourcesState> {
  private _selection: Selection;
  private _allItems: IDocument[];

  constructor(props: {}) {
    super(props);

    var default_dir_id = "dir-id";
    var default_drive_id = 'drive-id';

    this._allItems = _getDocuments();

    const columns: IColumn[] = [
      {
        key: 'column1',
        name: 'File Type',
        className: classNames.fileIconCell,
        iconClassName: classNames.fileIconHeaderIcon,
        ariaLabel: 'Column operations for File type, Press to sort on File type',
        iconName: 'Page',
        isIconOnly: true,
        fieldName: 'name',
        minWidth: 16,
        maxWidth: 16,
        onColumnClick: this._onColumnClick,
        onRender: (item: IDocument) => {
          return <img src={item.iconName} className={classNames.fileIconImg} alt={item.fileType + ' file icon'} />;
        }
      },
      {
        key: 'column2',
        name: 'Name',
        fieldName: 'name',
        minWidth: 210,
        maxWidth: 350,
        isRowHeader: true,
        isResizable: true,
        isSorted: true,
        isSortedDescending: false,
        sortAscendingAriaLabel: 'Sorted A to Z',
        sortDescendingAriaLabel: 'Sorted Z to A',
        onColumnClick: this._onColumnClick,
        data: 'string',
        isPadded: true
      },
      {
        key: 'column3',
        name: 'Date Modified',
        fieldName: 'dateModifiedValue',
        minWidth: 70,
        maxWidth: 90,
        isResizable: true,
        onColumnClick: this._onColumnClick,
        data: 'number',
        onRender: (item: IDocument) => {
          return <span>{item.dateModified}</span>;
        },
        isPadded: true
      },
      {
        key: 'column4',
        name: 'Modified By',
        fieldName: 'modifiedBy',
        minWidth: 70,
        maxWidth: 90,
        isResizable: true,
        isCollapsible: true,
        data: 'string',
        onColumnClick: this._onColumnClick,
        onRender: (item: IDocument) => {
          return <span>{item.modifiedBy}</span>;
        },
        isPadded: true
      },
      {
        key: 'column5',
        name: 'File Size',
        fieldName: 'fileSizeRaw',
        minWidth: 70,
        maxWidth: 90,
        isResizable: true,
        isCollapsible: true,
        data: 'number',
        onColumnClick: this._onColumnClick,
        onRender: (item: IDocument) => {
          return <span>{item.fileSize}</span>;
        }
      }
    ];

    this._selection = new Selection({
      onSelectionChanged: () => {
        this.setState({
          selectionDetails: this._getSelectionDetails()
        });
      }
    });

    this.state = {
      items: this._allItems,
      columns: columns,
      selectionDetails: this._getSelectionDetails(),
      isModalSelection: false,
      isCompactMode: false,
      announcedMessage: undefined,
    };
  }


  public render() : JSX.Element {
    const {columns, isCompactMode, items, selectionDetails, isModalSelection, announcedMessage } = this.state;

    return (
      <Fabric>
<Pivot onLinkClick={this.onLinkClick}>
         <PivotItem headerText="Alpha Resources">
         <div className={classNames.controlWrapper}>
          <TextField label="Filter by name:" onChange={this._onChangeText} styles={controlStyles} />
          <Announced message={`Number of items after filter applied: ${items.length}.`} />
        </div>
        <DetailsList
            items={items}
            compact={isCompactMode}
            columns={columns}
            selectionMode={SelectionMode.none}
            getKey={this._getKey}
            setKey="none"
            layoutMode={DetailsListLayoutMode.justified}
            isHeaderVisible={true}
            onItemInvoked={this._onItemInvoked}
          />
        </PivotItem>
         <PivotItem headerText="My OneDrive">
           <Nav
             onLinkClick={this.onNavClick}
             styles={{
               root: {
                 width: 208,
                 height: 350,
                 boxSizing: 'border-box',
                 border: '1px solid #eee',
                 overflowY: 'auto'
               }
             }}
             groups={[
               {
                 links: [
                   {
                     name: 'My Files',
                     url:'#',
                     key: 'key1',
                   },
                   {
                     name: 'Shared',
                     url: '#',
                     key: 'key2',
                   },
                   {
                     name: 'Recent',
                     url: '#',
                     key: 'key3',
                   }
                 ]
               }
             ]}
           />
        <div className={classNames.controlWrapper}>
          <TextField label="Filter by name:" onChange={this._onChangeText} styles={controlStyles} />
          <Announced message={`Number of items after filter applied: ${items.length}.`} />
        </div>
        <DetailsList
            items={items}
            compact={isCompactMode}
            columns={columns}
            selectionMode={SelectionMode.none}
            getKey={this._getKey}
            setKey="none"
            layoutMode={DetailsListLayoutMode.justified}
            isHeaderVisible={true}
            onItemInvoked={this._onItemInvoked}
          />
      </PivotItem>
      <PivotItem headerText="Team Resources">
      <div className={classNames.controlWrapper}>
          <TextField label="Filter by name:" onChange={this._onChangeText} styles={controlStyles} />
          <Announced message={`Number of items after filter applied: ${items.length}.`} />
        </div>
        <DetailsList
            items={items}
            compact={isCompactMode}
            columns={columns}
            selectionMode={SelectionMode.none}
            getKey={this._getKey}
            setKey="none"
            layoutMode={DetailsListLayoutMode.justified}
            isHeaderVisible={true}
            onItemInvoked={this._onItemInvoked}
          />
      </PivotItem>
      </Pivot>
      </Fabric>
    );
  }
  public onLinkClick(item: PivotItem): void {
    alert(item.props.headerText);
  }
  public onNavClick(ev: React.MouseEvent<HTMLElement>, item?: INavLink) {
    if (item) {
      alert(item.name);
    }
  }
  public componentDidUpdate(previousProps: any, previousState: IAlphaResourcesState) {
    if (previousState.isModalSelection !== this.state.isModalSelection && !this.state.isModalSelection) {
      this._selection.setAllSelected(false);
    }
  }

  private _getKey(item: any, index?: number): string {
    return item.key;
  }

  private _onChangeCompactMode = (ev: React.MouseEvent<HTMLElement>, checked: boolean): void => {
    this.setState({ isCompactMode: checked });
  };

  private _onChangeModalSelection = (ev: React.MouseEvent<HTMLElement>, checked: boolean): void => {
    this.setState({ isModalSelection: checked });
  };

  private _onChangeText = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text: string): void => {
    this.setState({
      items: text ? this._allItems.filter(i => i.name.toLowerCase().indexOf(text) > -1) : this._allItems
    });
  };

  private _onItemInvoked(item: any): void {
    alert(`Item invoked: ${item.name}`);
  }

  private _getSelectionDetails(): string {
    const selectionCount = this._selection.getSelectedCount();

    switch (selectionCount) {
      case 0:
        return 'No items selected';
      case 1:
        return '1 item selected: ' + (this._selection.getSelection()[0] as IDocument).name;
      default:
        return `${selectionCount} items selected`;
    }
  }

  private _onColumnClick = (ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
    const { columns, items } = this.state;
    const newColumns: IColumn[] = columns.slice();
    const currColumn: IColumn = newColumns.filter(currCol => column.key === currCol.key)[0];
    newColumns.forEach((newCol: IColumn) => {
      if (newCol === currColumn) {
        currColumn.isSortedDescending = !currColumn.isSortedDescending;
        currColumn.isSorted = true;
        this.setState({
          announcedMessage: `${currColumn.name} is sorted ${currColumn.isSortedDescending ? 'descending' : 'ascending'}`
        });
      } else {
        newCol.isSorted = false;
        newCol.isSortedDescending = true;
      }
    });
    const newItems = _copyAndSort(items, currColumn.fieldName!, currColumn.isSortedDescending);
    this.setState({
      columns: newColumns,
      items: newItems
    });
  };
}

function _copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): T[] {
  const key = columnKey as keyof T;
  return items.slice(0).sort((a: T, b: T) => ((isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1));
}

function _getDocuments(drive_type: any, dir_id: any, drive_id: any, action: any, item_id: any) {

  let graphUrl :string;
  const items: IDocument[] = [];

  if (drive_type == 'me' && !dir_id) {
    graphUrl = "/me/drive/root/children";
  }
  if (drive_type == 'shared' && !dir_id) {
    graphUrl = "/me/drive/sharedWithMe";
  }
  if (drive_type == 'recent' && !dir_id) {
    graphUrl = "/me/drive/recent";
  }
  if (drive_type == 'me' && dir_id) {
    graphUrl = "/me/" + dir_id + "/drive/root/children";
  }
  if (drive_type == 'shared' && dir_id && drive_id) {
    graphUrl = "/drives/" + drive_id + "/items/" + dir_id + "/children";
  }
  if (drive_type == 'recent' && dir_id) {
    graphUrl = "/drives/" + dir_id;
  }
  if (drive_type == 'groups' || drive_type == 'teams' && dir_id) {
    graphUrl = "/groups/" + dir_id + "/drive/root/children";
  }
//trigger if download required
  if(drive_type == 'me' && action=='download' && item_id){
    graphUrl="/me/drive/items/"+item_id+"/content";
  }

  //console.log(graphUrl)

    this.props.context.msGraphClientFactory
    .getClient()
    .then((client: MSGraphClient): void => {
      client
        .api(graphUrl)
        .version("v1.0")
        .get((error, response: any, rawResponse?: any) => {

          if (error) {
            console.error(error);
            return;
          }
          if (response) {
            //console.log(response);
            //var driveItems: Array<IDocument> = new Array<IDocument>();
            var contentType;
            response.value.map((item: any) => {
              if(item.folder){
                contentType = 'folder';
              }else{
                contentType = 'file';
              }
              items.push({
                key:item.id,
                name:item.name,
                value:item.name,
                iconName:'',
                fileType: '',
                webUrl:item.webUrl,
                fileSize: item.size,
                fileSizeRaw: item.size,
                dateModified: item.lastModifiedDateTime,
                modifiedBy: item.lastModifiedBy.user.displayName,
                dateModifiedValue: item.lastModifiedDateTime,
                contentType:contentType,
                parentId:item.parentReference.id,
                parentDriveId:item.parentReference.driveId,
              });
            });
            return items
            // Update the component state accordingly to the result  
            //console.log(items);
          }
        });
    });
}

正しい解決策はありません

他のヒント

In your AlphaRessourcesWebPart you trying to create en element of AlphaRessources and giving it properties. The Problem is, that your AlphaRessources Class is not accepting Properties other than {}.

Here's your code from AlphaResources.tsx:

export default class AlphaResources extends React.Component<{}, IAlphaResourcesState> {
      private _selection: Selection;
      private _allItems: IDocument[];

      constructor(props: {}) {
        super(props);
    ...}

...}

to give properties to your component it should be like the following:

export default class AlphaResources extends React.Component<IAlphaResourcesProps, IAlphaResourcesState> {
  private _selection: Selection;
  private _allItems: IDocument[];

  constructor(props: IAlphaResourcesProps) {
    super(props);
  ...}
...}
ライセンス: CC-BY-SA帰属
所属していません sharepoint.stackexchange
scroll top