When updating text while looping through SubTextParts TX Text Control .NET Server for ASP.NET
JavaScript API
SubTextPart Object
A SubTextPart object represents a user-defined part of a document.
using the given forEach TX Text Control .NET Server for ASP.NET
JavaScript API
Collection Object
forEach Method
Executes a callback function for each element.
method, the text will be replaced at wrong locations caused by JavaScript concurrency.

Consider the following document with 3 SubTextParts that have been inserted with the following code:

TXTextControl.subTextParts.add("MySubTextPart1",1,21,14);
TXTextControl.subTextParts.add("MySubTextPart2",11,57,14);
TXTextControl.subTextParts.add("SubTextPart3",12,93,12);
view raw test.js hosted with ❤ by GitHub

SubTextParts

Synchronous ForEach

The goal is to replace all SubTextParts that begin with the string "My". The obvious way to loop through the collection of SubTextParts would be by utilizing the forEach method in order to compare the name and to select and replace the text:

TXTextControl.subTextParts.forEach(stp => {
stp.getName(stpName => {
if (stpName.startsWith("My")) {
stp.getStart(start => {
stp.getLength(length => {
TXTextControl.select(start - 1, length, () => {
TXTextControl.selection.setText("New Text")
});
})
})
}
})
});
view raw test.js hosted with ❤ by GitHub

The problem with this code is that each function in the forEach loop is executed synchronously and results in the following output:

SubTextParts

The replaced text is inserted after the last found SubTextPart.

Using Promises

The solution is the usage of asynchronous functions with a JavaScript Promise we can wait for until the text has been replaced for each SubTextPart individually. The asynchronous function replaceSubTextParts that is shown in the following code uses a for loop to call the replaceText function sequentially.

async function replaceSubTextParts(startsWithText) {
TXTextControl.subTextParts.getCount(async function(count) {
for (var i = count - 1; i >= 0; i--) {
await replaceText(i, startsWithText);
}
})
}
function replaceText(i, startsWithText) {
return new Promise(resolve => {
TXTextControl.subTextParts.elementAt(i, function(stp) {
stp.getName(function(name) {
if (name.startsWith(startsWithText)) {
stp.getStart(function(start) {
stp.getLength(function(length) {
TXTextControl.select(start - 1, length - 1, function() {
TXTextControl.selection.setText("Replaced SubTextPart Text");
TXTextControl.focus();
TXTextControl.clear();
resolve(true);
})
})
})
} else resolve(false);
})
})
});
}
view raw test.js hosted with ❤ by GitHub

After the text has been replaced, the Promise is resolved successfully and the next SubTextPart can be checked. The following screenshot shows that the first two SubTextParts have been replaced successfully.

SubTextParts