Guest User

Untitled

a guest
Oct 17th, 2017
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.63 KB | None | 0 0
  1. Error: FactoryMethod.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
  2. at invariant (react-dom.js:17896)
  3.  
  4. import * as React from 'react';
  5. import * as ReactDom from 'react-dom';
  6. import { Version } from '@microsoft/sp-core-library';
  7. import {
  8. BaseClientSideWebPart,
  9. IPropertyPaneConfiguration,
  10. PropertyPaneTextField,
  11. PropertyPaneDropdown,
  12. IPropertyPaneDropdownOption,
  13. IPropertyPaneField,
  14. PropertyPaneLabel
  15. } from '@microsoft/sp-webpart-base';
  16.  
  17. import * as strings from 'FactoryMethodWebPartStrings';
  18. import FactoryMethod from './components/FactoryMethod';
  19. import { IFactoryMethodProps } from './components/IFactoryMethodProps';
  20. import { IFactoryMethodWebPartProps } from './IFactoryMethodWebPartProps';
  21. import * as lodash from '@microsoft/sp-lodash-subset';
  22. import List from './components/models/List';
  23. import { Environment, EnvironmentType } from '@microsoft/sp-core-library';
  24. import IDataProvider from './components/dataproviders/IDataProvider';
  25. import MockDataProvider from './test/MockDataProvider';
  26. import SharePointDataProvider from './components/dataproviders/SharepointDataProvider';
  27.  
  28. export default class FactoryMethodWebPart extends BaseClientSideWebPart<IFactoryMethodWebPartProps> {
  29. private _dropdownOptions: IPropertyPaneDropdownOption[];
  30. private _selectedList: List;
  31. private _disableDropdown: boolean;
  32. private _dataProvider: IDataProvider;
  33.  
  34. protected onInit(): Promise<void> {
  35. this.context.statusRenderer.displayLoadingIndicator(this.domElement, "Todo");
  36.  
  37. /*
  38. Create the appropriate data provider depending on where the web part is running.
  39. The DEBUG flag will ensure the mock data provider is not bundled with the web part when you package the
  40. solution for distribution, that is, using the --ship flag with the package-solution gulp command.
  41. */
  42. if (DEBUG && Environment.type === EnvironmentType.Local) {
  43. this._dataProvider = new MockDataProvider();
  44. } else {
  45. this._dataProvider = new SharePointDataProvider();
  46. this._dataProvider.webPartContext = this.context;
  47. }
  48.  
  49. this.openPropertyPane = this.openPropertyPane.bind(this);
  50.  
  51. /*
  52. Get the list of tasks lists from the current site and populate the property pane dropdown field with the values.
  53. */
  54. this.loadLists()
  55. .then(() => {
  56. /*
  57. If a list is already selected, then we would have stored the list Id in the associated web part property.
  58. 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
  59. in the property pane dropdown field.
  60. */
  61. if (this.properties.spListIndex) {
  62. this.setSelectedList(this.properties.spListIndex.toString());
  63. this.context.statusRenderer.clearLoadingIndicator(this.domElement);
  64. }
  65. });
  66.  
  67. return super.onInit();
  68. }
  69.  
  70. //Render method of the webpart, actually calls Component
  71. public render() {
  72.  
  73. const element: React.ReactElement<IFactoryMethodProps > = React.createElement(
  74. FactoryMethod,
  75. {
  76. spHttpClient: this.context.spHttpClient,
  77. siteUrl: this.context.pageContext.web.absoluteUrl,
  78. listName: this.properties.listName,
  79. dataProvider: this._dataProvider
  80. }
  81. );
  82.  
  83. ReactDom.render(element, this.domElement);
  84. }
  85.  
  86. //Loads lists from the site and filld the dropdown
  87. private loadLists(): Promise<any> {
  88. return this._dataProvider.getLists()
  89. .then((lists: List[]) => {
  90. // Disable dropdown field if there are no results from the server.
  91. this._disableDropdown = lists.length === 0;
  92. if (lists.length !== 0) {
  93. this._dropdownOptions = lists.map((list: List) => {
  94. return {
  95. key: list.Id,
  96. text: list.Title
  97. };
  98. });
  99. }
  100. });
  101. }
  102.  
  103. protected get dataVersion(): Version {
  104. return Version.parse('1.0');
  105. }
  106.  
  107. protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
  108. /*
  109. Check the property path to see which property pane feld changed. If the property path matches the dropdown, then we set that list
  110. as the selected list for the web part.
  111. */
  112. if (propertyPath === 'spListIndex') {
  113. this.setSelectedList(newValue);
  114. }
  115.  
  116. /*
  117. Finally, tell property pane to re-render the web part.
  118. This is valid for reactive property pane.
  119. */
  120. super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
  121. }
  122.  
  123. //Sets the selected list based on the selection from the dropdownlist
  124. private setSelectedList(value: string) {
  125. const selectedIndex: number = lodash.findIndex(this._dropdownOptions,
  126. (item: IPropertyPaneDropdownOption) => item.key === value
  127. );
  128.  
  129. const selectedDropDownOption: IPropertyPaneDropdownOption = this._dropdownOptions[selectedIndex];
  130.  
  131. if (selectedDropDownOption) {
  132. this._selectedList = {
  133. Title: selectedDropDownOption.text,
  134. Id: selectedDropDownOption.key.toString()
  135. };
  136.  
  137. this._dataProvider.selectedList = this._selectedList;
  138. }
  139. }
  140.  
  141.  
  142. //We add fields dinamycally to the property pane, in this case its only the list field which we will render
  143. private getGroupFields(): IPropertyPaneField<any>[] {
  144. const fields: IPropertyPaneField<any>[] = [];
  145.  
  146. //We add the options from the dropdownoptions variable that was populated during init to the dropdown here.
  147. fields.push(PropertyPaneDropdown('spListIndex', {
  148. label: "Select a list",
  149. disabled: this._disableDropdown,
  150. options: this._dropdownOptions
  151. }));
  152.  
  153. /*
  154. When we do not have any lists returned from the server, we disable the dropdown. If that is the case,
  155. we also add a label field displaying the appropriate message.
  156. */
  157. if (this._disableDropdown) {
  158. fields.push(PropertyPaneLabel(null, {
  159. text: 'Could not find tasks lists in your site. Create one or more tasks list and then try using the web part.'
  160. }));
  161. }
  162.  
  163. return fields;
  164. }
  165.  
  166. private openPropertyPane(): void {
  167. this.context.propertyPane.open();
  168. }
  169.  
  170. protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
  171. return {
  172. pages: [
  173. {
  174. header: {
  175. description: strings.PropertyPaneDescription
  176. },
  177. groups: [
  178. {
  179. groupName: strings.BasicGroupName,
  180. /*
  181. Instead of creating the fields here, we call a method that will return the set of property fields to render.
  182. */
  183. groupFields: this.getGroupFields()
  184. }
  185. ]
  186. }
  187. ]
  188. };
  189. }
  190. }
  191.  
  192.  
  193. //#region Imports
  194. import * as React from 'react';
  195. import styles from './FactoryMethod.module.scss';
  196. import { IFactoryMethodProps } from './IFactoryMethodProps';
  197. import {
  198. IDetailsListItemState,
  199. IDetailsNewsListItemState,
  200. IDetailsDirectoryListItemState,
  201. IDetailsAnnouncementListItemState,
  202. IFactoryMethodState
  203. } from './IFactoryMethodState';
  204. import { IListItem } from './models/IListItem';
  205. import { IAnnouncementListItem } from './models/IAnnouncementListItem';
  206. import { INewsListItem } from './models/INewsListItem';
  207. import { IDirectoryListItem } from './models/IDirectoryListItem';
  208. import { escape } from '@microsoft/sp-lodash-subset';
  209. import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
  210. import { ListItemFactory} from './ListItemFactory';
  211. import { TextField } from 'office-ui-fabric-react/lib/TextField';
  212. import {
  213. DetailsList,
  214. DetailsListLayoutMode,
  215. Selection,
  216. IColumn
  217. } from 'office-ui-fabric-react/lib/DetailsList';
  218. import { MarqueeSelection } from 'office-ui-fabric-react/lib/MarqueeSelection';
  219. import { autobind } from 'office-ui-fabric-react/lib/Utilities';
  220. //#endregion
  221.  
  222. export default class FactoryMethod extends React.Component<IFactoryMethodProps, IFactoryMethodState> {
  223. private listItemEntityTypeName: string = undefined;
  224. private _selection: Selection;
  225.  
  226. constructor(props: IFactoryMethodProps, state: any) {
  227. super(props);
  228. this.setInitialState();
  229. }
  230.  
  231. public componentWillReceiveProps(nextProps: IFactoryMethodProps) {
  232. this.listItemEntityTypeName = undefined;
  233. this.setInitialState();
  234. }
  235.  
  236. public setInitialState(): void {
  237. this.state = {
  238. type: 'ListItem',
  239. status: this.listNotConfigured(this.props)
  240. ? 'Please configure list in Web Part properties'
  241. : 'Ready',
  242. DetailsListItemState:{
  243. columns:[],
  244. items:[]
  245. },
  246. DetailsNewsListItemState:{
  247. columns:[],
  248. items:[]
  249. },
  250. DetailsDirectoryListItemState:{
  251. columns:[],
  252. items:[]
  253. },
  254. DetailsAnnouncementListItemState:{
  255. columns:[],
  256. items:[]
  257. },
  258. };
  259. }
  260.  
  261. //Reusable inline component
  262. public ListMarqueeSelection = (itemState: {columns: IColumn[], items: IListItem[] }) => (
  263. <div>
  264. <MarqueeSelection selection={ this._selection }>
  265. <DetailsList
  266. items={ itemState.items }
  267. columns={ itemState.columns }
  268. setKey='set'
  269. layoutMode={ DetailsListLayoutMode.fixedColumns }
  270. selection={ this._selection }
  271. selectionPreservedOnEmptyClick={ true }
  272. compact={ true }>
  273. </DetailsList>
  274. </MarqueeSelection>
  275. </div>
  276. )
  277.  
  278. public render(): React.ReactElement<IFactoryMethodProps> {
  279. let { type,
  280. status,
  281. DetailsListItemState,
  282. DetailsNewsListItemState,
  283. DetailsDirectoryListItemState,
  284. DetailsAnnouncementListItemState } = this.state;
  285.  
  286. switch(this.props.listName)
  287. {
  288. case "List":
  289. return <this.ListMarqueeSelection items={this.state.DetailsListItemState.items} columns={this.state.DetailsListItemState.columns} />;
  290. case "News":
  291. return <this.ListMarqueeSelection items={this.state.DetailsNewsListItemState.items} columns={this.state.DetailsNewsListItemState.columns}/>;
  292. case "Announcements":
  293. return <this.ListMarqueeSelection items={this.state.DetailsAnnouncementListItemState.items} columns={this.state.DetailsAnnouncementListItemState.columns}/>;
  294. case "Directory":
  295. return <this.ListMarqueeSelection items={this.state.DetailsDirectoryListItemState.items} columns={this.state.DetailsDirectoryListItemState.columns}/>;
  296. default:
  297. return undefined;
  298. }
  299. }
  300.  
  301. //Read items using factory method pattern and sets state accordingly
  302. private readItems(): void {
  303. this.setState({
  304. status: 'Loading all items...'
  305. });
  306.  
  307. this.setStateWithList();
  308. }
  309.  
  310. private setStateWithList(): void {
  311. const factory = new ListItemFactory();
  312. const items = factory.getItems(this.props.spHttpClient,
  313. this.props.siteUrl, this.props.listName);
  314. const keyPart = this.props.listName === 'Items' ? '' : this.props.listName;
  315.  
  316. // the explicit specification of the type argument `keyof {}` is bad and
  317. // it should not be required.
  318. this.setState<keyof {}>({
  319. status: `Successfully loaded ${items.length} items`,
  320. ['Details' + keyPart + 'ListItemState'] : {
  321. items,
  322. columns: [
  323. ]
  324. }
  325. });
  326. }
  327.  
  328. //Gets the selected item details
  329. private getSelectionDetails(): string {
  330. let selectionCount = this._selection.getSelectedCount();
  331.  
  332. switch (selectionCount) {
  333. case 0:
  334. return 'No items selected';
  335. case 1:
  336. return '1 item selected: ' + (this._selection.getSelection()[0] as any).name;
  337. default:
  338. return `${selectionCount} items selected`;
  339. }
  340. }
  341.  
  342. private listNotConfigured(props: IFactoryMethodProps): boolean {
  343. return props.listName === undefined ||
  344. props.listName === null ||
  345. props.listName.length === 0;
  346. }
  347.  
  348. }
Add Comment
Please, Sign In to add comment