Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Error: FactoryMethod.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
- at invariant (react-dom.js:17896)
- import * as React from 'react';
- import * as ReactDom from 'react-dom';
- import { Version } from '@microsoft/sp-core-library';
- import {
- BaseClientSideWebPart,
- IPropertyPaneConfiguration,
- PropertyPaneTextField,
- PropertyPaneDropdown,
- IPropertyPaneDropdownOption,
- IPropertyPaneField,
- PropertyPaneLabel
- } from '@microsoft/sp-webpart-base';
- import * as strings from 'FactoryMethodWebPartStrings';
- import FactoryMethod from './components/FactoryMethod';
- import { IFactoryMethodProps } from './components/IFactoryMethodProps';
- import { IFactoryMethodWebPartProps } from './IFactoryMethodWebPartProps';
- import * as lodash from '@microsoft/sp-lodash-subset';
- import List from './components/models/List';
- import { Environment, EnvironmentType } from '@microsoft/sp-core-library';
- import IDataProvider from './components/dataproviders/IDataProvider';
- import MockDataProvider from './test/MockDataProvider';
- import SharePointDataProvider from './components/dataproviders/SharepointDataProvider';
- export default class FactoryMethodWebPart extends BaseClientSideWebPart<IFactoryMethodWebPartProps> {
- private _dropdownOptions: IPropertyPaneDropdownOption[];
- private _selectedList: List;
- private _disableDropdown: boolean;
- private _dataProvider: IDataProvider;
- protected onInit(): Promise<void> {
- this.context.statusRenderer.displayLoadingIndicator(this.domElement, "Todo");
- /*
- Create the appropriate data provider depending on where the web part is running.
- The DEBUG flag will ensure the mock data provider is not bundled with the web part when you package the
- solution for distribution, that is, using the --ship flag with the package-solution gulp command.
- */
- if (DEBUG && Environment.type === EnvironmentType.Local) {
- this._dataProvider = new MockDataProvider();
- } else {
- this._dataProvider = new SharePointDataProvider();
- this._dataProvider.webPartContext = this.context;
- }
- this.openPropertyPane = this.openPropertyPane.bind(this);
- /*
- Get the list of tasks lists from the current site and populate the property pane dropdown field with the values.
- */
- this.loadLists()
- .then(() => {
- /*
- If a list is already selected, then we would have stored the list Id in the associated web part property.
- So, check to see if we do have a selected list for the web part. If we do, then we set that as the selected list
- in the property pane dropdown field.
- */
- if (this.properties.spListIndex) {
- this.setSelectedList(this.properties.spListIndex.toString());
- this.context.statusRenderer.clearLoadingIndicator(this.domElement);
- }
- });
- return super.onInit();
- }
- //Render method of the webpart, actually calls Component
- public render() {
- const element: React.ReactElement<IFactoryMethodProps > = React.createElement(
- FactoryMethod,
- {
- spHttpClient: this.context.spHttpClient,
- siteUrl: this.context.pageContext.web.absoluteUrl,
- listName: this.properties.listName,
- dataProvider: this._dataProvider
- }
- );
- ReactDom.render(element, this.domElement);
- }
- //Loads lists from the site and filld the dropdown
- private loadLists(): Promise<any> {
- return this._dataProvider.getLists()
- .then((lists: List[]) => {
- // Disable dropdown field if there are no results from the server.
- this._disableDropdown = lists.length === 0;
- if (lists.length !== 0) {
- this._dropdownOptions = lists.map((list: List) => {
- return {
- key: list.Id,
- text: list.Title
- };
- });
- }
- });
- }
- protected get dataVersion(): Version {
- return Version.parse('1.0');
- }
- protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
- /*
- Check the property path to see which property pane feld changed. If the property path matches the dropdown, then we set that list
- as the selected list for the web part.
- */
- if (propertyPath === 'spListIndex') {
- this.setSelectedList(newValue);
- }
- /*
- Finally, tell property pane to re-render the web part.
- This is valid for reactive property pane.
- */
- super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
- }
- //Sets the selected list based on the selection from the dropdownlist
- private setSelectedList(value: string) {
- const selectedIndex: number = lodash.findIndex(this._dropdownOptions,
- (item: IPropertyPaneDropdownOption) => item.key === value
- );
- const selectedDropDownOption: IPropertyPaneDropdownOption = this._dropdownOptions[selectedIndex];
- if (selectedDropDownOption) {
- this._selectedList = {
- Title: selectedDropDownOption.text,
- Id: selectedDropDownOption.key.toString()
- };
- this._dataProvider.selectedList = this._selectedList;
- }
- }
- //We add fields dinamycally to the property pane, in this case its only the list field which we will render
- private getGroupFields(): IPropertyPaneField<any>[] {
- const fields: IPropertyPaneField<any>[] = [];
- //We add the options from the dropdownoptions variable that was populated during init to the dropdown here.
- fields.push(PropertyPaneDropdown('spListIndex', {
- label: "Select a list",
- disabled: this._disableDropdown,
- options: this._dropdownOptions
- }));
- /*
- When we do not have any lists returned from the server, we disable the dropdown. If that is the case,
- we also add a label field displaying the appropriate message.
- */
- if (this._disableDropdown) {
- fields.push(PropertyPaneLabel(null, {
- text: 'Could not find tasks lists in your site. Create one or more tasks list and then try using the web part.'
- }));
- }
- return fields;
- }
- private openPropertyPane(): void {
- this.context.propertyPane.open();
- }
- protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
- return {
- pages: [
- {
- header: {
- description: strings.PropertyPaneDescription
- },
- groups: [
- {
- groupName: strings.BasicGroupName,
- /*
- Instead of creating the fields here, we call a method that will return the set of property fields to render.
- */
- groupFields: this.getGroupFields()
- }
- ]
- }
- ]
- };
- }
- }
- //#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,
- 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';
- //#endregion
- export default class FactoryMethod extends React.Component<IFactoryMethodProps, IFactoryMethodState> {
- private listItemEntityTypeName: string = undefined;
- private _selection: Selection;
- constructor(props: IFactoryMethodProps, state: any) {
- super(props);
- this.setInitialState();
- }
- public componentWillReceiveProps(nextProps: IFactoryMethodProps) {
- this.listItemEntityTypeName = undefined;
- this.setInitialState();
- }
- public setInitialState(): void {
- this.state = {
- type: 'ListItem',
- status: this.listNotConfigured(this.props)
- ? 'Please configure list in Web Part properties'
- : 'Ready',
- DetailsListItemState:{
- columns:[],
- items:[]
- },
- DetailsNewsListItemState:{
- columns:[],
- items:[]
- },
- DetailsDirectoryListItemState:{
- columns:[],
- items:[]
- },
- DetailsAnnouncementListItemState:{
- columns:[],
- items:[]
- },
- };
- }
- //Reusable inline component
- public ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => (
- <div>
- <MarqueeSelection selection={ this._selection }>
- <DetailsList
- items={ itemState.items }
- columns={ itemState.columns }
- setKey='set'
- layoutMode={ DetailsListLayoutMode.fixedColumns }
- selection={ this._selection }
- selectionPreservedOnEmptyClick={ true }
- compact={ true }>
- </DetailsList>
- </MarqueeSelection>
- </div>
- )
- public render(): React.ReactElement<IFactoryMethodProps> {
- let { type,
- status,
- DetailsListItemState,
- DetailsNewsListItemState,
- DetailsDirectoryListItemState,
- DetailsAnnouncementListItemState } = this.state;
- switch(this.props.listName)
- {
- case "List":
- return <this.ListMarqueeSelection items={this.state.DetailsListItemState.items} columns={this.state.DetailsListItemState.columns} />;
- case "News":
- return <this.ListMarqueeSelection items={this.state.DetailsNewsListItemState.items} columns={this.state.DetailsNewsListItemState.columns}/>;
- case "Announcements":
- return <this.ListMarqueeSelection items={this.state.DetailsAnnouncementListItemState.items} columns={this.state.DetailsAnnouncementListItemState.columns}/>;
- case "Directory":
- return <this.ListMarqueeSelection items={this.state.DetailsDirectoryListItemState.items} columns={this.state.DetailsDirectoryListItemState.columns}/>;
- default:
- return undefined;
- }
- }
- //Read items using factory method pattern and sets state accordingly
- private readItems(): void {
- this.setState({
- status: 'Loading all items...'
- });
- this.setStateWithList();
- }
- private setStateWithList(): void {
- const factory = new ListItemFactory();
- const items = factory.getItems(this.props.spHttpClient,
- this.props.siteUrl, this.props.listName);
- const keyPart = this.props.listName === 'Items' ? '' : 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: [
- ]
- }
- });
- }
- //Gets the selected item details
- 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;
- }
- }
Add Comment
Please, Sign In to add comment