Skip to content

Commit c8eb0cf

Browse files
committed
#805 - support of Calculated fields in
1 parent 2c87001 commit c8eb0cf

File tree

6 files changed

+98
-24
lines changed

6 files changed

+98
-24
lines changed

src/common/SPEntities.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface ISPField {
2323
RichText?: boolean;
2424
SchemaXml?: string;
2525
LookupDisplayUrl?: string;
26+
TypeAsString?: string;
27+
ResultType?: string;
2628
}
2729

2830
/**
@@ -166,4 +168,4 @@ export interface ICultureInfo {
166168
name: string;
167169
dateTimeFormat: ICultureDateTimeFormat;
168170
numberFormat: ICultureNumberFormat;
169-
}
171+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { ISPField } from '../../common/SPEntities';
2+
13
export interface IListItemPickerState {
24
noresultsFoundText: string;
35
showError: boolean;
46
errorMessage: string;
57
suggestionsHeaderText:string;
68
selectedItems?: any[];
9+
field?: ISPField;
710
}

src/controls/listItemPicker/ListItemPicker.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export class ListItemPicker extends React.Component<IListItemPickerProps, IListI
2929
this._spservice = new SPservice(this.props.context);
3030
}
3131

32+
public componentDidMount() {
33+
this.loadField(this.props);
34+
}
35+
3236
public componentWillReceiveProps(nextProps: IListItemPickerProps) {
3337
let newSelectedItems: any[] | undefined;
3438
if (this.props.listId !== nextProps.listId) {
@@ -41,6 +45,12 @@ export class ListItemPicker extends React.Component<IListItemPickerProps, IListI
4145
this.setState({
4246
selectedItems: newSelectedItems
4347
});
48+
49+
if (this.props.listId !== nextProps.listId
50+
|| this.props.columnInternalName !== nextProps.columnInternalName
51+
|| this.props.webUrl !== nextProps.webUrl) {
52+
this.loadField(nextProps);
53+
}
4454
}
4555

4656
/**
@@ -135,11 +145,14 @@ export class ListItemPicker extends React.Component<IListItemPickerProps, IListI
135145
*/
136146
private loadListItems = async (filterText: string): Promise<{ key: string; name: string }[]> => {
137147
let { listId, columnInternalName, keyColumnInternalName, webUrl, filter, substringSearch } = this.props;
148+
const {
149+
field
150+
} = this.state;
138151
let arrayItems: { key: string; name: string }[] = [];
139152
let keyColumn: string = keyColumnInternalName || 'Id';
140153

141154
try {
142-
let listItems = await this._spservice.getListItems(filterText, listId, columnInternalName, keyColumn, webUrl, filter, substringSearch); // JJ - 20200613 - find by substring as an option
155+
let listItems = await this._spservice.getListItems(filterText, listId, columnInternalName, field, keyColumn, webUrl, filter, substringSearch); // JJ - 20200613 - find by substring as an option
143156
// Check if the list had items
144157
if (listItems.length > 0) {
145158
for (const item of listItems) {
@@ -157,4 +170,16 @@ export class ListItemPicker extends React.Component<IListItemPickerProps, IListI
157170
return null;
158171
}
159172
}
173+
174+
private loadField = async (props: IListItemPickerProps): Promise<void> => {
175+
this.setState({
176+
field: undefined
177+
});
178+
179+
const field = await this._spservice.getField(props.listId, props.columnInternalName, props.webUrl);
180+
181+
this.setState({
182+
field
183+
});
184+
}
160185
}

src/services/ISPService.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ISPLists } from "../common/SPEntities";
1+
import { ISPField, ISPLists } from "../common/SPEntities";
22

33
export enum LibsOrderBy {
44
Id = 1,
@@ -21,5 +21,6 @@ export interface ISPService {
2121
* @param options Options used to order and filter during the API query
2222
*/
2323
getLibs(options?: ILibsOptions): Promise<ISPLists>;
24-
getListItems?(filterText: string, listId: string, internalColumnName: string, keyInternalColumnName?: string, webUrl?: string) : Promise<any[]>;
24+
getListItems?(filterText: string, listId: string, internalColumnName: string, field: ISPField, keyInternalColumnName?: string, webUrl?: string) : Promise<any[]>;
25+
getField: (listId: string, internalColumnName: string, webUrl?: string) => Promise<ISPField | undefined>;
2526
}

src/services/SPService.ts

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ISPService, ILibsOptions, LibsOrderBy } from "./ISPService";
2-
import { ISPLists } from "../common/SPEntities";
2+
import { ISPField, ISPLists } from "../common/SPEntities";
33
import { WebPartContext } from "@microsoft/sp-webpart-base";
44
import { ExtensionContext } from "@microsoft/sp-extension-base";
55
import { SPHttpClient, ISPHttpClientOptions } from "@microsoft/sp-http";
@@ -10,7 +10,32 @@ export default class SPService implements ISPService {
1010

1111
constructor(private _context: WebPartContext | ExtensionContext, webAbsoluteUrl?: string) {
1212
this._webAbsoluteUrl = webAbsoluteUrl ? webAbsoluteUrl : this._context.pageContext.web.absoluteUrl;
13-
}
13+
}
14+
15+
public getField = async (listId: string, internalColumnName: string, webUrl?: string): Promise<ISPField | undefined> => {
16+
try {
17+
const webAbsoluteUrl = !webUrl ? this._webAbsoluteUrl : webUrl;
18+
const apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/fields/getByInternalNameOrTitle('${internalColumnName}')`;
19+
const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1);
20+
if (data.ok) {
21+
const results = await data.json();
22+
if (results) {
23+
const field = results as ISPField;
24+
25+
if (field.TypeAsString === 'Calculated') {
26+
const resultTypeRegEx = /ResultType="(\w+)"/gmi;
27+
const resultTypeMatch = resultTypeRegEx.exec(field.SchemaXml);
28+
29+
field.ResultType = resultTypeMatch[1];
30+
}
31+
32+
return field;
33+
}
34+
}
35+
} catch (error) {
36+
return Promise.reject(error);
37+
}
38+
}
1439

1540
/**
1641
* Get lists or libraries
@@ -50,15 +75,27 @@ export default class SPService implements ISPService {
5075
/**
5176
* Get List Items
5277
*/
53-
public async getListItems(filterText: string, listId: string, internalColumnName: string, keyInternalColumnName?: string, webUrl?: string, filter?: string, substringSearch: boolean = false ): Promise<any[]> {
78+
public async getListItems(filterText: string, listId: string, internalColumnName: string, field: ISPField | undefined, keyInternalColumnName?: string, webUrl?: string, filter?: string, substringSearch: boolean = false): Promise<any[]> {
5479
let returnItems: any[];
55-
const filterStr = substringSearch ? // JJ - 20200613 - find by substring as an option
56-
`substringof('${encodeURIComponent(filterText.replace("'","''"))}',${internalColumnName})${filter ? ' and ' + filter : ''}`
57-
: `startswith(${internalColumnName},'${encodeURIComponent(filterText.replace("'","''"))}')${filter ? ' and ' + filter : ''}`; //string = filterList ? `and ${filterList}` : '';
80+
const webAbsoluteUrl = !webUrl ? this._webAbsoluteUrl : webUrl;
81+
let apiUrl = '';
82+
let isPost = false;
83+
84+
if (field && field.TypeAsString === 'Calculated') { // for calculated fields we need to use CAML query
85+
const camlQuery = `<View><Query><Where>${substringSearch ? '<Contains>' : '<BeginsWith>'}<FieldRef Name="${internalColumnName}"/><Value Type="${field.ResultType}">${filterText}</Value>${substringSearch ? '</Contains>' : '</BeginsWith>'}</Where></Query></View>`;
86+
87+
apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/GetItems(query=@v1)?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&@v1=${JSON.stringify({ ViewXml: camlQuery })}`;
88+
isPost = true;
89+
}
90+
else {
91+
const filterStr = substringSearch ? // JJ - 20200613 - find by substring as an option
92+
`substringof('${encodeURIComponent(filterText.replace("'", "''"))}',${internalColumnName})${filter ? ' and ' + filter : ''}`
93+
: `startswith(${internalColumnName},'${encodeURIComponent(filterText.replace("'", "''"))}')${filter ? ' and ' + filter : ''}`; //string = filterList ? `and ${filterList}` : '';
94+
apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&$filter=${filterStr}`;
95+
}
96+
5897
try {
59-
const webAbsoluteUrl = !webUrl ? this._webAbsoluteUrl : webUrl;
60-
const apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&$filter=${filterStr}`;
61-
const data = await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1);
98+
const data = isPost ? await this._context.spHttpClient.post(apiUrl, SPHttpClient.configurations.v1, {}) : await this._context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1);
6299
if (data.ok) {
63100
const results = await data.json();
64101
if (results && results.value && results.value.length > 0) {
@@ -74,16 +111,16 @@ export default class SPService implements ISPService {
74111

75112

76113

77-
/**
78-
* Gets list items for list item picker
79-
* @param filterText
80-
* @param listId
81-
* @param internalColumnName
82-
* @param [keyInternalColumnName]
83-
* @param [webUrl]
84-
* @param [filterList]
85-
* @returns list items for list item picker
86-
*/
114+
/**
115+
* Gets list items for list item picker
116+
* @param filterText
117+
* @param listId
118+
* @param internalColumnName
119+
* @param [keyInternalColumnName]
120+
* @param [webUrl]
121+
* @param [filterList]
122+
* @returns list items for list item picker
123+
*/
87124
public async getListItemsForListItemPicker(
88125
filterText: string,
89126
listId: string,

src/services/SPServiceMock.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ISPService, ILibsOptions } from "./ISPService";
2-
import { ISPLists } from "../common/SPEntities";
2+
import { ISPField, ISPLists } from "../common/SPEntities";
33

44
export default class SPServiceMock implements ISPService {
55
private _includeDelay?: boolean;
@@ -9,6 +9,12 @@ export default class SPServiceMock implements ISPService {
99
this._includeDelay = includeDelay;
1010
this._delayTimeout = delayTimeout || 500;
1111
}
12+
public getListItems(filterText: string, listId: string, internalColumnName: string, field: ISPField, keyInternalColumnName?: string, webUrl?: string): Promise<any[]> {
13+
throw new Error("Method not implemented.");
14+
}
15+
public getField = async (listId: string, internalColumnName: string, webUrl?: string): Promise<ISPField | undefined> => {
16+
return;
17+
}
1218

1319
/**
1420
* The mock lists to present to the local workbench

0 commit comments

Comments
 (0)