Skip to content

Commit aaf3d1f

Browse files
authored
Merge pull request #1181 from re-motion/feature/RM-9265-Context-information-for-autocomplete-pagination
RM-9265 Add optional context information for pagination in BocAutoCompleteReferenceValue
2 parents b33d993 + 87fb125 commit aaf3d1f

File tree

10 files changed

+80
-25
lines changed

10 files changed

+80
-25
lines changed

Remotion/ObjectBinding/Web.ClientScript/Scripts/BocAutoCompleteReferenceValue.UI.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ namespace Remotion.BocAutoCompleteReferenceValue
4242
{
4343
Type: 'ValueList';
4444
Values: Remotion.BocAutoCompleteReferenceValue.Item[];
45+
Context: Nullable<string>;
4546
HasMoreSearchResults: boolean;
4647
}
4748

4849
export interface BocAutoCompleteReferenceValueCacheableSearchResult
4950
{
5051
cacheRow: CacheRow;
5152
hasMoreSearchResults: boolean;
53+
context: Nullable<string>;
5254
}
5355

5456
export type Item = {
@@ -796,7 +798,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
796798
return true;
797799
}
798800

799-
function requestMoreData(term: string, offset: number): Promise<BocAutoCompleteReferenceValueCacheableSearchResult> {
801+
function requestMoreData(term: string, offset: number, context: Nullable<string>): Promise<BocAutoCompleteReferenceValueCacheableSearchResult> {
800802
return new Promise((resolve, reject) => {
801803
const successHandler = (term: string, result: BocAutoCompleteReferenceValueCacheableSearchResult) => {
802804
resolve(result);
@@ -806,7 +808,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
806808
reject();
807809
};
808810

809-
requestData(term, offset, successHandler, failureHandler);
811+
requestData(term, offset, context, successHandler, failureHandler);
810812
});
811813
}
812814

@@ -846,7 +848,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
846848
closeDropDownListAndSetValue(state.previousValue, false);
847849
};
848850

849-
requestData(searchString, 0, successHandler, failureHandler);
851+
requestData(searchString, 0, null, successHandler, failureHandler);
850852
} else {
851853
stopLoading();
852854
select.hide();
@@ -939,7 +941,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
939941
}
940942
};
941943

942-
function requestData(term: string, offset: number, success: (term: string, result: BocAutoCompleteReferenceValueCacheableSearchResult) => void, failure: (term: string) => void) {
944+
function requestData(term: string, offset: number, context: Nullable<string>, success: (term: string, result: BocAutoCompleteReferenceValueCacheableSearchResult) => void, failure: (term: string) => void) {
943945
// re-motion: cancel an already running request
944946
abortRequest();
945947

@@ -969,7 +971,8 @@ namespace Remotion.BocAutoCompleteReferenceValue
969971
const params: Dictionary<unknown> = {
970972
searchString: term,
971973
completionSetOffset: offset,
972-
completionSetCount: options.max
974+
completionSetCount: options.max,
975+
context: context
973976
};
974977
for (const propertyName in options.extraParams)
975978
params[propertyName] = options.extraParams[propertyName];
@@ -1023,7 +1026,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
10231026
executingRequest = null;
10241027
let parsed: Nullable<CacheRowEntry> = null;
10251028
if (result != null) {
1026-
const resultArray: BocAutoCompleteReferenceValueSearchResultWithValueList = { Type: "ValueList", Values: [result], HasMoreSearchResults: false };
1029+
const resultArray: BocAutoCompleteReferenceValueSearchResultWithValueList = { Type: "ValueList", Values: [result], HasMoreSearchResults: false, Context: null };
10271030
const parsedArray = options.parse(resultArray);
10281031
parsed = parsedArray.cacheRow![0];
10291032
}
@@ -1091,6 +1094,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
10911094
if (existingData && existingData.cacheRow.length === offset) {
10921095
existingData.cacheRow.push(...value.cacheRow);
10931096
existingData.hasMoreSearchResults = value.hasMoreSearchResults;
1097+
existingData.context = value.context;
10941098
}
10951099
// ignore offset data that does not append to an existing cache line
10961100
}
@@ -1119,7 +1123,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
11191123
private readonly options: Options;
11201124
private readonly input: HTMLInputElement;
11211125
private readonly select: (value: boolean) => void;
1122-
private readonly loadMoreData: (term: string, offset: number) => Promise<BocAutoCompleteReferenceValueCacheableSearchResult>
1126+
private readonly loadMoreData: (term: string, offset: number, context: Nullable<string>) => Promise<BocAutoCompleteReferenceValueCacheableSearchResult>
11231127
private readonly config: SelectConfig;
11241128

11251129
private readonly ac_data: WeakMap<HTMLElement, CacheRowEntry> = new WeakMap();
@@ -1128,7 +1132,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
11281132
options: Options,
11291133
input: HTMLInputElement,
11301134
select: (value: boolean) => void,
1131-
loadMoreData: (term: string, offset: number) => Promise<BocAutoCompleteReferenceValueCacheableSearchResult>,
1135+
loadMoreData: (term: string, offset: number, context: Nullable<string>) => Promise<BocAutoCompleteReferenceValueCacheableSearchResult>,
11321136
config: SelectConfig) {
11331137
this.options = options;
11341138
this.input = input;
@@ -1145,6 +1149,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
11451149
private active: number = -1;
11461150
private data: Nullable<CacheRow> = null;
11471151
private term: string = "";
1152+
private context: Nullable<string> = null;
11481153
private needsInit: boolean = true;
11491154
private element: Nullable<HTMLElement> = undefined!;
11501155
private announcementElement: HTMLElement = undefined!;
@@ -1359,12 +1364,13 @@ namespace Remotion.BocAutoCompleteReferenceValue
13591364

13601365
this.clearAnnouncements();
13611366

1362-
const loadingPromise = this.loadMoreData(this.term, this.listItems.length);
1367+
const loadingPromise = this.loadMoreData(this.term, this.listItems.length, this.context);
13631368
this.addLoadingAnnouncement();
13641369
this.isLoadingMoreData = true;
13651370
this.placeholderItem.classList.add("ac_placeholder_loading");
13661371
loadingPromise.then(searchResult => {
13671372
this.addListItems(searchResult.cacheRow, searchResult.hasMoreSearchResults);
1373+
this.context = searchResult.context;
13681374
this.isLoadingMoreData = false;
13691375
this.addLoadedAnnouncement();
13701376
}, _ => {
@@ -1511,6 +1517,7 @@ namespace Remotion.BocAutoCompleteReferenceValue
15111517
this.init();
15121518
this.data = [];
15131519
this.term = q;
1520+
this.context = d.context;
15141521
this.listItems = [];
15151522
this.list.innerHTML = '';
15161523
this.addListItems(d.cacheRow, d.hasMoreSearchResults);

Remotion/ObjectBinding/Web.ClientScript/Scripts/BocAutoCompleteReferenceValue.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ class BocAutoCompleteReferenceValue //TODO RM-7715 - Make the TypeScript classes
157157

158158
return {
159159
cacheRow,
160-
hasMoreSearchResults: valueList.HasMoreSearchResults
160+
hasMoreSearchResults: valueList.HasMoreSearchResults,
161+
context: typeof valueList.Context === "string" ? valueList.Context : null
161162
};
162163
}
163164
else

Remotion/ObjectBinding/Web.Development.WebTesting.IntegrationTests/BocAutoCompleteReferenceValueControlObjectTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ public void TestGetSearchServiceResults ()
505505
Assert.That(searchResults.Count, Is.EqualTo(4));
506506
Assert.That(searchResults[0].DisplayName, Is.EqualTo("D, "));
507507

508-
var offsetSearchResults = bocAutoComplete.GetSearchServiceResults("D", 1, 4);
508+
var offsetSearchResults = bocAutoComplete.GetSearchServiceResults("D", 1, 4, "1");
509509
Assert.That(offsetSearchResults, Is.EqualTo(searchResults.Skip(1)));
510510

511511
searchResults = bocAutoComplete.GetSearchServiceResults("unexistentValue", 0, 5);

Remotion/ObjectBinding/Web.Development.WebTesting.TestSite.Shared/Controls/BocAutoCompleteReferenceValueWebService.asmx.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
using System;
1818
using System.Collections.Generic;
1919
using System.ComponentModel;
20+
using System.Diagnostics;
2021
using System.Linq;
2122
using System.Web;
2223
using System.Web.Script.Services;
2324
using System.Web.Services;
25+
using JetBrains.Annotations;
2426
using Remotion.ObjectBinding.Sample;
2527
using Remotion.ObjectBinding.Web.Services;
2628
using Remotion.ObjectBinding.Web.UI.Controls;
@@ -70,6 +72,7 @@ public BocAutoCompleteReferenceValueSearchResult Search (
7072
string searchString,
7173
int completionSetOffset,
7274
int? completionSetCount,
75+
string context,
7376
string businessObjectClass,
7477
string businessObjectProperty,
7578
string businessObject,
@@ -78,6 +81,13 @@ public BocAutoCompleteReferenceValueSearchResult Search (
7881
if (searchString == "throw")
7982
throw new InvalidOperationException("I'm always going to throw an exception if you search for 'throw'!");
8083

84+
// In this service we assume that context is always the same as the offset (or null if offset == 0)
85+
// The following asserts ensure that the client behaves correctly and sends back the correct context information
86+
if (completionSetOffset == 0 && context != null)
87+
throw new InvalidOperationException($"The specified context '{context}' was not expected.");
88+
if (completionSetOffset != 0 && context != completionSetOffset.ToString())
89+
throw new InvalidOperationException($"The specified context '{context}' did not match the expected context '{completionSetOffset}'.");
90+
8191
if (searchString == "testlist")
8292
{
8393
var count = completionSetCount ?? 10;
@@ -86,7 +96,11 @@ public BocAutoCompleteReferenceValueSearchResult Search (
8696
.Take(count)
8797
.Select(e => new BusinessObjectWithIdentityProxy { DisplayName = $"testlist Person {e}", UniqueIdentifier = e.ToString() })
8898
.ToArray();
89-
return BocAutoCompleteReferenceValueSearchResult.CreateForValueList(resultItems, completionSetOffset + count < 20);
99+
100+
return BocAutoCompleteReferenceValueSearchResult.CreateForValueList(
101+
resultItems,
102+
completionSetOffset + count < 20,
103+
(completionSetOffset + count).ToString());
90104
}
91105

92106
var persons = new List<BusinessObjectWithIdentityProxy>();
@@ -104,7 +118,7 @@ public BocAutoCompleteReferenceValueSearchResult Search (
104118

105119
var resultArray = filteredPersons.Skip(completionSetOffset).Take(completionSetCount ?? int.MaxValue).ToArray();
106120
var hasMoreSearchResults = completionSetCount.HasValue && resultArray.Length >= completionSetCount.Value;
107-
return BocAutoCompleteReferenceValueSearchResult.CreateForValueList(resultArray, hasMoreSearchResults);
121+
return BocAutoCompleteReferenceValueSearchResult.CreateForValueList(resultArray, hasMoreSearchResults, (completionSetOffset + (completionSetCount ?? 0)).ToString());
108122
}
109123

110124
[WebMethod]
@@ -119,7 +133,7 @@ public BusinessObjectWithIdentityProxy SearchExact (
119133
if (searchString == "throw")
120134
throw new InvalidOperationException("I'm always going to throw an exception if you search for 'throw'!");
121135

122-
var resultWithValueList = Search(searchString, 0, 2, businessObjectClass, businessObjectProperty, businessObject, args);
136+
var resultWithValueList = Search(searchString, 0, 2, null, businessObjectClass, businessObjectProperty, businessObject, args);
123137
var result = ((BocAutoCompleteReferenceValueSearchResultWithValueList)resultWithValueList).Values;
124138
if (result.Length == 0)
125139
return null;

Remotion/ObjectBinding/Web.Development.WebTesting/ControlObjects/BocAutoCompleteReferenceValueControlObject.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,15 @@ public DropDownMenuControlObject GetDropDownMenu ()
156156
/// <param name="searchText">Text to search for.</param>
157157
/// <param name="completionSetOffset">Auto completion set offset.</param>
158158
/// <param name="completionSetCount">Auto completion set count.</param>
159+
/// <param name="context">Arbitrary context information that was provided by a previous request.</param>
159160
/// <returns>The completion set as list of <see cref="SearchServiceResultItem"/> or an empty list if the completion set has been empty.</returns>
160-
public IReadOnlyList<SearchServiceResultItem> GetSearchServiceResults ([NotNull] string searchText, int completionSetOffset, int completionSetCount)
161+
public IReadOnlyList<SearchServiceResultItem> GetSearchServiceResults ([NotNull] string searchText, int completionSetOffset, int completionSetCount, string? context = null)
161162
{
162163
ArgumentUtility.CheckNotNullOrEmpty("searchText", searchText);
163164

164165
var inputScopeID = GetInputScopeID();
165166

166-
var searchServiceRequestScript = CreateAutoCompleteSearchServiceRequest(inputScopeID, searchText, completionSetOffset, completionSetCount);
167+
var searchServiceRequestScript = CreateAutoCompleteSearchServiceRequest(inputScopeID, searchText, completionSetOffset, completionSetCount, context);
167168
var response = (IReadOnlyDictionary<string, object>)Context.Browser.Driver.ExecuteScript(searchServiceRequestScript, Scope);
168169
return ParseSearchServiceResponse(
169170
response,
@@ -297,7 +298,7 @@ private string CreateAutoCompleteExactSearchServiceRequest (
297298
ArgumentUtility.CheckNotNullOrEmpty("autoCompleteTextValueInputFieldId", autoCompleteTextValueInputFieldId);
298299
ArgumentUtility.CheckNotNullOrEmpty("searchText", searchText);
299300

300-
return CreateAutoCompleteSearchServiceRequestScript(autoCompleteTextValueInputFieldId, searchText, "serviceMethodSearchExact", null, null);
301+
return CreateAutoCompleteSearchServiceRequestScript(autoCompleteTextValueInputFieldId, searchText, "serviceMethodSearchExact", null, null, null);
301302
}
302303

303304
/// <summary>
@@ -307,24 +308,33 @@ private string CreateAutoCompleteExactSearchServiceRequest (
307308
/// <param name="searchText">The search text.</param>
308309
/// <param name="completionSetOffset"></param>
309310
/// <param name="completionSetCount">The maximum size of the returned auto completion set.</param>
311+
/// <param name="context">Arbitrary context information that was provided by a previous request.</param>
310312
private string CreateAutoCompleteSearchServiceRequest (
311313
[NotNull] string autoCompleteTextValueInputFieldId,
312314
[NotNull] string searchText,
313315
int completionSetOffset,
314-
int completionSetCount)
316+
int completionSetCount,
317+
string? context)
315318
{
316319
ArgumentUtility.CheckNotNullOrEmpty("autoCompleteTextValueInputFieldId", autoCompleteTextValueInputFieldId);
317320
ArgumentUtility.CheckNotNullOrEmpty("searchText", searchText);
318321

319-
return CreateAutoCompleteSearchServiceRequestScript(autoCompleteTextValueInputFieldId, searchText, "serviceMethodSearch", completionSetOffset, completionSetCount);
322+
return CreateAutoCompleteSearchServiceRequestScript(
323+
autoCompleteTextValueInputFieldId,
324+
searchText,
325+
"serviceMethodSearch",
326+
completionSetOffset,
327+
completionSetCount,
328+
context);
320329
}
321330

322331
private string CreateAutoCompleteSearchServiceRequestScript (
323332
[NotNull] string autoCompleteTextValueInputFieldId,
324333
[NotNull] string searchText,
325334
[NotNull] string searchMethod,
326335
int? completionSetOffset,
327-
int? completionSetCount)
336+
int? completionSetCount,
337+
string? context)
328338
{
329339
ArgumentUtility.CheckNotNullOrEmpty("autoCompleteTextValueInputFieldId", autoCompleteTextValueInputFieldId);
330340
ArgumentUtility.CheckNotNullOrEmpty("searchText", searchText);
@@ -338,6 +348,10 @@ private string CreateAutoCompleteSearchServiceRequestScript (
338348
? string.Format("data['completionSetCount'] = {0};", completionSetCount.Value)
339349
: string.Empty;
340350

351+
var contextScriptPart = context != null
352+
? $"data['context'] = '{context}';"
353+
: "data['context'] = null;";
354+
341355
return $@"
342356
CallWebService = function() {{
343357
var input = document.getElementById('{autoCompleteTextValueInputFieldId}');
@@ -347,6 +361,7 @@ private string CreateAutoCompleteSearchServiceRequestScript (
347361
data['searchString'] = options.searchString;
348362
{setCompletionSetOffsetScriptPart}
349363
{setCompletionSetCountScriptPart}
364+
{contextScriptPart}
350365
351366
data = Sys.Serialization.JavaScriptSerializer.serialize(data);
352367

Remotion/ObjectBinding/Web.Test/IndividualControlTests/BocAutoCompleteReferenceValueWebService.asmx.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using System.Web;
2323
using System.Web.Script.Services;
2424
using System.Web.Services;
25+
using JetBrains.Annotations;
2526
using Remotion.ObjectBinding;
2627
using Remotion.ObjectBinding.Sample;
2728
using Remotion.ObjectBinding.Web.Services;
@@ -209,6 +210,7 @@ public BocAutoCompleteReferenceValueSearchResult Search (
209210
string searchString,
210211
int completionSetOffset,
211212
int? completionSetCount,
213+
string context,
212214
string businessObjectClass,
213215
string businessObjectProperty,
214216
string businessObject,
@@ -274,7 +276,7 @@ public BusinessObjectWithIdentityProxy SearchExact (string searchString, string
274276
Thread.Sleep(delay);
275277
}
276278

277-
var resultWithValueList = Search(searchString, 0, 2, businessObjectClass, businessObjectProperty, businessObject, args);
279+
var resultWithValueList = Search(searchString, 0, 2, null, businessObjectClass, businessObjectProperty, businessObject, args);
278280
var result = ((BocAutoCompleteReferenceValueSearchResultWithValueList)resultWithValueList).Values;
279281
if (result.Length == 0)
280282
return null;

Remotion/ObjectBinding/Web/Services/BocAutoCompleteReferenceValueSearchResult.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@ public abstract class BocAutoCompleteReferenceValueSearchResult
2424
/// <summary>
2525
/// Creates a new <see cref="BocAutoCompleteReferenceValueSearchResult"/> based on the <paramref name="values"/>.
2626
/// </summary>
27-
public static BocAutoCompleteReferenceValueSearchResult CreateForValueList (BusinessObjectWithIdentityProxy[] values, bool hasMoreSearchResults) =>
28-
new BocAutoCompleteReferenceValueSearchResultWithValueList(values, hasMoreSearchResults);
27+
public static BocAutoCompleteReferenceValueSearchResult CreateForValueList (
28+
BusinessObjectWithIdentityProxy[] values,
29+
bool hasMoreSearchResults,
30+
string? context = null)
31+
{
32+
return new BocAutoCompleteReferenceValueSearchResultWithValueList(values, hasMoreSearchResults, context);
33+
}
2934

3035
/// <summary>
3136
/// The discriminator for different <see cref="BocAutoCompleteReferenceValueSearchResult"/> variants, used during Javascript deserialization.

Remotion/ObjectBinding/Web/Services/BocAutoCompleteReferenceValueSearchResultWithValueList.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ public class BocAutoCompleteReferenceValueSearchResultWithValueList : BocAutoCom
3636
/// </summary>
3737
public bool HasMoreSearchResults { get; }
3838

39-
internal BocAutoCompleteReferenceValueSearchResultWithValueList (BusinessObjectWithIdentityProxy[] values, bool hasMoreSearchResults)
39+
/// <summary>
40+
/// Arbitrary context information that will be sent back to the server as part of the next pagination request.
41+
/// </summary>
42+
public string? Context { get; }
43+
44+
internal BocAutoCompleteReferenceValueSearchResultWithValueList (BusinessObjectWithIdentityProxy[] values, bool hasMoreSearchResults, string? context)
4045
{
4146
Values = values;
4247
HasMoreSearchResults = hasMoreSearchResults;
48+
Context = context;
4349
}
4450
}
4551
}

Remotion/ObjectBinding/Web/Services/IBocAutoCompleteReferenceValueWebService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public interface IBocAutoCompleteReferenceValueWebService : IBocReferenceValueBa
3535
/// <param name="completionSetCount">
3636
/// The maximum number of items to be returned or <see langword="null" /> if the search service implementation can define the result set size.
3737
/// </param>
38+
/// <param name="context">
39+
/// Arbitrary context information that will be sent back to the server as part of the next pagination request.
40+
/// </param>
3841
/// <param name="businessObjectClass">
3942
/// The <see cref="IBusinessObjectClass.Identifier"/> of the <see cref="IBusinessObjectClass"/> the control is bound to or <see langword="null" />.
4043
/// This value is either the <see cref="IBusinessObject.BusinessObjectClass"/> of the bound <see cref="IBusinessObject"/> or the
@@ -56,6 +59,7 @@ BocAutoCompleteReferenceValueSearchResult Search (
5659
string searchString,
5760
int completionSetOffset,
5861
int? completionSetCount,
62+
string? context,
5963
string? businessObjectClass,
6064
string? businessObjectProperty,
6165
string? businessObject,

0 commit comments

Comments
 (0)