From 883ecbbfc3693a4a96c305ae930941fb41a506ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20S=C3=B6th?= Date: Thu, 7 Dec 2023 15:27:11 +0100 Subject: [PATCH 1/5] More than 100 lookup values --- src/services/SPService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/SPService.ts b/src/services/SPService.ts index 466d785c8..05ac64b42 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -259,7 +259,7 @@ export default class SPService implements ISPService { return filteredItems; } - apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}`; + apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}&$top=5000`; isPost = false; //eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -529,7 +529,7 @@ export default class SPService implements ISPService { public async getLookupValue(listId: string, listItemID: number, fieldName: string, lookupFieldName: string | undefined, webUrl?: string): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})/?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}`; + const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})/?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}&$top=5000`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { @@ -549,7 +549,7 @@ export default class SPService implements ISPService { public async getLookupValues(listId: string, listItemID: number, fieldName: string, lookupFieldName: string | undefined, webUrl?: string): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}`; + const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}&$top=5000`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { From e91b04e2aeb522141d8f01bfb75cbb1a62bf76b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20S=C3=B6th?= Date: Thu, 7 Dec 2023 15:29:22 +0100 Subject: [PATCH 2/5] remove top5000 from getLookupValue --- src/services/SPService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/SPService.ts b/src/services/SPService.ts index 05ac64b42..e06596e12 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -529,7 +529,7 @@ export default class SPService implements ISPService { public async getLookupValue(listId: string, listItemID: number, fieldName: string, lookupFieldName: string | undefined, webUrl?: string): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})/?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}&$top=5000`; + const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})/?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { @@ -549,7 +549,7 @@ export default class SPService implements ISPService { public async getLookupValues(listId: string, listItemID: number, fieldName: string, lookupFieldName: string | undefined, webUrl?: string): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any try { const webAbsoluteUrl = !webUrl ? this._context.pageContext.web.absoluteUrl : webUrl; - const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}&$top=5000`; + const apiUrl = `${webAbsoluteUrl}/_api/web/lists(@listId)/items(${listItemID})?@listId=guid'${encodeURIComponent(listId)}'&$select=${fieldName}/ID,${fieldName}/${lookupFieldName || 'Title'}&$expand=${fieldName}`; const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1); if (data.ok) { From f53187739b004e0c0ba8c15f557a0ef01db15a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20S=C3=B6th?= Date: Thu, 7 Dec 2023 15:39:54 +0100 Subject: [PATCH 3/5] Format date in lookup fields --- src/services/SPService.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/services/SPService.ts b/src/services/SPService.ts index e06596e12..0f02161cf 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -535,7 +535,12 @@ export default class SPService implements ISPService { if (data.ok) { const result = await data.json(); if (result && result[fieldName]) { - return [{ key: result[fieldName].ID, name: result[fieldName][lookupFieldName || 'Title'] }]; + let value = result[fieldName][lookupFieldName || 'Title']; + const dateVal = Date.parse(value); + if (dateVal !== NaN) { + value = new Date(value).toLocaleDateString(); + } + return [{ key: result[fieldName].ID, name: value }]; } } @@ -559,14 +564,24 @@ export default class SPService implements ISPService { const isArray = Array.isArray(result[fieldName]); //multiselect lookups are arrays if (isArray) { - result[fieldName].forEach(element => { - lookups.push({ key: element.ID, name: element[lookupFieldName || 'Title'] }); + result[fieldName].forEach(element => { + let value = element[fieldName][lookupFieldName || 'Title']; + const dateVal = Date.parse(value); + if (dateVal !== NaN) { + value = new Date(value).toLocaleDateString(); + } + lookups.push({ key: element.ID, name: value }); }); } //single select lookups are objects else { const singleItem = result[fieldName]; - lookups.push({ key: singleItem.ID, name: singleItem[lookupFieldName || 'Title'] }); + let value = singleItem[fieldName][lookupFieldName || 'Title']; + const dateVal = Date.parse(value); + if (dateVal !== NaN) { + value = new Date(value).toLocaleDateString(); + } + lookups.push({ key: singleItem.ID, name: value }); } return lookups; } From 627d1324d283ae360d416e1e4ad144cedcaa4d4e Mon Sep 17 00:00:00 2001 From: Niels Soeth Date: Tue, 27 Feb 2024 14:30:10 +0100 Subject: [PATCH 4/5] add itemsQueryCountLimit property to ListItemPicker, DynamicField --- .../docs/controls/ListItemPicker.md | 44 ++++++++++--------- .../dynamicForm/dynamicField/DynamicField.tsx | 5 ++- .../dynamicField/IDynamicFieldProps.ts | 1 + .../listItemPicker/IListItemPickerProps.ts | 2 +- .../listItemPicker/ListItemPicker.tsx | 4 +- src/services/SPService.ts | 5 ++- 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/docs/documentation/docs/controls/ListItemPicker.md b/docs/documentation/docs/controls/ListItemPicker.md index 0878d08d5..7034c1cea 100644 --- a/docs/documentation/docs/controls/ListItemPicker.md +++ b/docs/documentation/docs/controls/ListItemPicker.md @@ -18,6 +18,7 @@ Here is an example of the control: ```TypeScript import { ListItemPicker } from '@pnp/spfx-controls-react/lib/ListItemPicker'; ``` + - Use the `ListItemPicker` control in your code as follows: ```TypeScript @@ -41,30 +42,31 @@ private onSelectedItem(data: { key: string; name: string }[]) { } } ``` + ## Implementation The `ListItemPicker` control can be configured with the following properties: - -| Property | Type | Required | Description | -| ---- | ---- | ---- | ---- | -| columnInternalName | string | yes | InternalName of column to search and get values. | -| keyColumnInternalName | string | no | InternalName of column to use as the key for the selection. Must be a column with unique values. Default: Id | -| context | BaseComponentContext | yes | SPFx web part or extention context | -| listId | string | yes | Guid or title of the list. | -| itemLimit | number | yes | Number of items which can be selected | -| onSelectedItem | (items: any[]) => void | yes | Callback function which returns the selected items. | -| className | string | no | ClassName for the picker. | -| webUrl | string | no | URL of the site. By default it uses the current site URL. | -| defaultSelectedItems | any[] | no | Initial items that have already been selected and should appear in the people picker. | -| suggestionsHeaderText | string | no | The text that should appear at the top of the suggestion box. | -| noResultsFoundText | string | no | The text that should appear when no results are returned. | -| disabled | boolean | no | Specifies if the control is disabled or not. | -| filter | string | no | condition to filter list Item, same as $filter ODATA parameter| -| orderBy | string | no | condition to order by list Item, same as $orderby ODATA parameter| -| placeholder | string | no | Short text hint to display in empty picker | -| substringSearch | boolean | no | Specifies if substring search should be used | -| label | string | no | Specifies the text describing the ListItemPicker. | -| enableDefaultSuggestions | boolean | no | Enable default suggestions. All options are displayed by default when clicking on the control. | +| Property | Type | Required | Description | +| ------------------------ | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | +| columnInternalName | string | yes | InternalName of column to search and get values. | +| keyColumnInternalName | string | no | InternalName of column to use as the key for the selection. Must be a column with unique values. Default: Id | +| context | BaseComponentContext | yes | SPFx web part or extention context | +| listId | string | yes | Guid or title of the list. | +| itemLimit | number | yes | Number of items which can be selected | +| onSelectedItem | (items: any[]) => void | yes | Callback function which returns the selected items. | +| className | string | no | ClassName for the picker. | +| webUrl | string | no | URL of the site. By default it uses the current site URL. | +| defaultSelectedItems | any[] | no | Initial items that have already been selected and should appear in the people picker. | +| suggestionsHeaderText | string | no | The text that should appear at the top of the suggestion box. | +| noResultsFoundText | string | no | The text that should appear when no results are returned. | +| disabled | boolean | no | Specifies if the control is disabled or not. | +| filter | string | no | condition to filter list Item, same as $filter ODATA parameter | +| orderBy | string | no | condition to order by list Item, same as $orderby ODATA parameter | +| placeholder | string | no | Short text hint to display in empty picker | +| substringSearch | boolean | no | Specifies if substring search should be used | +| label | string | no | Specifies the text describing the ListItemPicker. | +| enableDefaultSuggestions | boolean | no | Enable default suggestions. All options are displayed by default when clicking on the control. | +| itemsQueryCountLimit | number | no | Number of items to display in a lookup field | ![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ListItemPicker) diff --git a/src/controls/dynamicForm/dynamicField/DynamicField.tsx b/src/controls/dynamicForm/dynamicField/DynamicField.tsx index b8641731a..1192e312c 100644 --- a/src/controls/dynamicForm/dynamicField/DynamicField.tsx +++ b/src/controls/dynamicForm/dynamicField/DynamicField.tsx @@ -80,7 +80,8 @@ export class DynamicField extends React.Component { this.onChange(newValue); }} context={context} + itemsQueryCountLimit={itemsQueryCountLimit} /> {descriptionEl} {errorTextEl} @@ -258,6 +260,7 @@ export class DynamicField extends React.Component { this.onChange(newValue); }} context={context} + itemsQueryCountLimit={itemsQueryCountLimit} /> {descriptionEl} {errorTextEl} diff --git a/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts b/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts index 6aa5d62cc..d39157c6e 100644 --- a/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts +++ b/src/controls/dynamicForm/dynamicField/IDynamicFieldProps.ts @@ -38,4 +38,5 @@ export interface IDynamicFieldProps { maximumValue?: number; minimumValue?: number; showAsPercentage?: boolean; + itemsQueryCountLimit?: number; } diff --git a/src/controls/listItemPicker/IListItemPickerProps.ts b/src/controls/listItemPicker/IListItemPickerProps.ts index 2a64c68fa..2dc30a8a8 100644 --- a/src/controls/listItemPicker/IListItemPickerProps.ts +++ b/src/controls/listItemPicker/IListItemPickerProps.ts @@ -35,5 +35,5 @@ export interface IListItemPickerProps { */ enableDefaultSuggestions?: boolean; styles? : IBasePickerStyles; - + itemsQueryCountLimit?: number; } diff --git a/src/controls/listItemPicker/ListItemPicker.tsx b/src/controls/listItemPicker/ListItemPicker.tsx index 928bbcdb9..9d6a60bd4 100644 --- a/src/controls/listItemPicker/ListItemPicker.tsx +++ b/src/controls/listItemPicker/ListItemPicker.tsx @@ -175,7 +175,7 @@ export class ListItemPicker extends React.Component => { - const { columnInternalName, keyColumnInternalName, webUrl, filter, orderBy, substringSearch } = this.props; + const { columnInternalName, keyColumnInternalName, webUrl, filter, orderBy, substringSearch, itemsQueryCountLimit } = this.props; const { field, safeListId } = this.state; @@ -183,7 +183,7 @@ export class ListItemPicker extends React.Component 0) { for (const item of listItems) { diff --git a/src/services/SPService.ts b/src/services/SPService.ts index 0f02161cf..e184867a2 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -221,7 +221,8 @@ export default class SPService implements ISPService { filterString?: string, substringSearch: boolean = false, orderBy?: string, - cacheInterval: number = 1): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any + cacheInterval: number = ICON_GENERIC_16, + top?: number): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any const webAbsoluteUrl = !webUrl ? this._webAbsoluteUrl : webUrl; let apiUrl = ''; let isPost = false; @@ -259,7 +260,7 @@ export default class SPService implements ISPService { return filteredItems; } - apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}&$top=5000`; + apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}${top ? `&$top=${top}` : ''}; isPost = false; //eslint-disable-next-line @typescript-eslint/no-explicit-any From 2ac28a255986fcaee94aea3b08086a291f7847f9 Mon Sep 17 00:00:00 2001 From: Niels Soeth Date: Wed, 29 May 2024 14:23:06 +0200 Subject: [PATCH 5/5] update NaN check, add $top, change param order --- src/services/SPService.ts | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/services/SPService.ts b/src/services/SPService.ts index 688388a96..1887d539b 100644 --- a/src/services/SPService.ts +++ b/src/services/SPService.ts @@ -221,8 +221,8 @@ export default class SPService implements ISPService { filterString?: string, substringSearch: boolean = false, orderBy?: string, - cacheInterval: number = ICON_GENERIC_16, - top?: number): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any + top?: number, + cacheInterval: number = 1): Promise { // eslint-disable-line @typescript-eslint/no-explicit-any const webAbsoluteUrl = !webUrl ? this._webAbsoluteUrl : webUrl; let apiUrl = ''; let isPost = false; @@ -249,7 +249,7 @@ export default class SPService implements ISPService { const filterStr = substringSearch ? // JJ - 20200613 - find by substring as an option `${filterText ? `substringof('${encodeURIComponent(filterText.replace("'", "''"))}',${internalColumnName})` : ''}${filterString ? (filterText ? ' and ' : '') + filterString : ''}` : `${filterText ? `startswith(${internalColumnName},'${encodeURIComponent(filterText.replace("'", "''"))}')` : ''}${filterString ? (filterText ? ' and ' : '') + filterString : ''}`; //string = filterList ? `and ${filterList}` : ''; - apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&$filter=${filterStr}&$orderby=${orderBy}`; + apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&$filter=${filterStr}&$orderby=${orderBy}${top ? `&$top=${top}` : ''}`; } else { // we need to get FieldValuesAsText and cache them const mapKey = `${webAbsoluteUrl}##${listId}##${internalColumnName}##${keyInternalColumnName || 'Id'}`; @@ -260,7 +260,7 @@ export default class SPService implements ISPService { return filteredItems; } - apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}${top ? `&$top=${top}` : ''}; + apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName},FieldValuesAsText/${internalColumnName}&$expand=FieldValuesAsText&$orderby=${orderBy}${filterString ? '&$filter=' + filterString : ''}${top ? `&$top=${top}` : ''}`; isPost = false; //eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -536,12 +536,7 @@ export default class SPService implements ISPService { if (data.ok) { const result = await data.json(); if (result && result[fieldName]) { - let value = result[fieldName][lookupFieldName || 'Title']; - const dateVal = Date.parse(value); - if (dateVal !== NaN) { - value = new Date(value).toLocaleDateString(); - } - return [{ key: result[fieldName].ID, name: value }]; + return [{ key: result[fieldName].ID, name: result[fieldName][lookupFieldName || 'Title'] }]; } } @@ -566,9 +561,9 @@ export default class SPService implements ISPService { //multiselect lookups are arrays if (isArray) { result[fieldName].forEach(element => { - let value = element[fieldName][lookupFieldName || 'Title']; + let value = element[lookupFieldName || 'Title']; const dateVal = Date.parse(value); - if (dateVal !== NaN) { + if (!Number.isNaN(dateVal)) { value = new Date(value).toLocaleDateString(); } lookups.push({ key: element.ID, name: value }); @@ -577,9 +572,9 @@ export default class SPService implements ISPService { //single select lookups are objects else { const singleItem = result[fieldName]; - let value = singleItem[fieldName][lookupFieldName || 'Title']; + let value = singleItem[lookupFieldName || 'Title']; const dateVal = Date.parse(value); - if (dateVal !== NaN) { + if (!Number.isNaN(dateVal)) { value = new Date(value).toLocaleDateString(); } lookups.push({ key: singleItem.ID, name: value });