In my previous posts Creating and removing N:N relationship between Dataverse records using Javascript and Power Pages Web API I showed how we can leverage the Power Pages Web API to handle single N:N associate and disassociate requests for a custom N:N relationship between Accounts and Contacts, and also in the following post Generic JavaScript Functions to Associate and Disassociate Dataverse records using the Power Pages Web API, I brought revisited versions of functions to handle the Associate and Disassociate process for any table.
In case we want to handle multiple requests, we need to develop some extra handling and implement function calling for arrays of IDs.
In this post I will bring a new function to handle multiple requests.
Prerequisites
Handle Multiple Disassociate and Associate Requests
The HandleMultipleAssociateDisassociate function uses recursion to handle multiple associate and disassociate operations. The gist of the idea is, the function receives an object containing arrays of ids to be processed, it will process one item, and call itself again with the remaining ids to be processed (excluding the one just processed – either to associate or disassociate).
The function takes in an object multipleRequestData as a parameter.
This object contains information about the records to associate and disassociate, as well as callback functions to execute on success and error.
The format of the multipleRequestData object is as follows:
The function then checks if there are any record IDs left to associate or disassociate by calling the pop method on the idsToAssociate and idsToDisassociate arrays, respectively.
function HandleMultipleAssociateDisassociate(multipleRequestData)
{
let idToAssociate = multipleRequestData.idsToAssociate ? multipleRequestData.idsToAssociate.pop() : null;
let idToDisassociate = idToAssociate ? null : (multipleRequestData.idsToDisassociate ? multipleRequestData.idsToDisassociate.pop() : null);
if (idToAssociate) {
let thisRecord = {
recordId: idToAssociate,
recordTable: multipleRequestData.recordTable,
relatedRecordId: multipleRequestData.relatedRecordId,
relatedRecordTable: multipleRequestData.relatedRecordTable};
AssociateRecords({
...thisRecord,
relationshipSchemaName: multipleRequestData.relationshipSchemaName,
onSucess: function (data, textStatus, xhr) {
console.log(`Associated ${multipleRequestData.recordTable} ${idToAssociate} to ${multipleRequestData.relatedRecordTable} ${multipleRequestData.relatedRecordId}`);
HandleMultipleAssociateDisassociate(multipleRequestData);
},
onError: function (xhr, textStatus, errorThrown) {
multipleRequestData.onAssociateError(xhr, textStatus, errorThrown, thisRecord);
}
})
} else if (idToDisassociate) {
let thisRecord = {
recordId: idToDisassociate,
recordTable: multipleRequestData.recordTable,
relatedRecordId: multipleRequestData.relatedRecordId,
relatedRecordTable: multipleRequestData.relatedRecordTable,
}
DisassociateRecords({
...thisRecord,
relationshipSchemaName: multipleRequestData.relationshipSchemaName,
onSucess: function (data, textStatus, xhr) {
console.log(`Disassociated ${multipleRequestData.recordTable} ${idToDisassociate} from ${multipleRequestData.relatedRecordTable} ${multipleRequestData.relatedRecordId}`)
HandleMultipleAssociateDisassociate(multipleRequestData);
},
onError: function (xhr, textStatus, errorThrown) {
multipleRequestData.onDisassociateError(xhr, textStatus, errorThrown, thisRecord);
}
});
} else {
if(multipleRequestData.runAfterAllSuceeded) { multipleRequestData.runAfterAllSuceeded() };
}
}
Handling Errors
In this sample, when errors happen we are simply alerting the errors, but you can run some actions inside of this function to flag items that were associated/disassociated and maybe retry failed actions (you will need to implement maybe a ‘processing queue log’ in combination to the HandleMultipleAssociateDisassociate function above):
function onWebAPIAssociateError (xhr, textStatus, errorThrown, objectData) {
alert(`${textStatus} error while saving data`, `Error associating ${objectData.recordTable} record (${objectData.recordId}) to ${objectData.relatedRecordTable} record (${objectData.relatedRecordId}) : ${xhr.responseJSON.error.message}`);
console.log(xhr);
}
function onWebAPIDisassociateError(xhr, textStatus, errorThrown, objectData) {
alert(`${textStatus} error while saving data`, `Error disassociating ${objectData.recordTable} record (${objectData.recordId}) to ${objectData.relatedRecordTable} record (${objectData.relatedRecordId}) : ${xhr.responseJSON.error.message}`);
}
Sample function call
The idsToAssociate and idsToDisassociate properties need to be in Array format. If you obtained them as collections (such as via jQuery DOM queries), you might need to convert them. Using the notation below simplifies this conversion.
Run a function call like the below, and the code will take care of handling your relationships
let runAfterAllSuceeded = function () {
alert("Assignments saved successfully!");
}
HandleMultipleAssociateDisassociate({
idsToAssociate: [...usersToAdd],
idsToDisassociate: [...usersToRemove],
recordTable: "contacts",
relatedRecordId: accountID,
relatedRecordTable: "accounts",
relationshipSchemaName: "prefix_Account_Contact_Contact",
runAfterAllSuceeded: runAfterAllSuceeded,
onAssociateError: onWebAPIAssociateError,
onDisassociateError: onWebAPIDisassociateError
});
In summary:
The HandleMultipleAssociateDisassociate function uses recursion to process multiple associate and disassociate operations by calling itself with an updated multipleRequestData object until there are no more record IDs left to process.
As the bulk associate/disassociate requests are not handled as a single transaction, you will need to handle possible failed requests yourself by implementing the functions onWebAPIAssociateError / onWebAPIDisassociateError that you send as property of the object sent as parameter to this funcion.
References
Associate and disassociate tables by using the Web API – Microsoft Learn
The post Handle multiple N:N records associate and disassociate requests with JavaScript and Power Pages Web API appeared first on michelcarlo.