SharePoint Framework (SPFX) onInit() Promises
-
29-12-2020 - |
Domanda
I'm in the process of getting my feet wet with SPFx Application Customizers and have a question regarding the use of promises in TS.
Before my onRender() method is called inside of onInit(), I need to call two functions using PnP JS:
- The first retrieves the web title
- The second fetches some user-profile metadata
How can I ensure these two functions complete before the onRender() logic is executed?
public onInit(): Promise<void> {
Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);
pnp.sp.web.select("Title").get().then(w => {
this._webTitle = w.Title;
});
pnp.sp.profiles.myProperties.get().then(function (result) {
var userProperties = result.UserProfileProperties;
console.log(userProperties);
userProperties.forEach(function (property) {
if (property.Key == "PreferredName") {
this._userDisplayName = property.Value;
console.log('user display name: ' + this._userDisplayName);
}
});
}).catch(function (error) {
console.log("Error: " + error);
});
return Promise.resolve<void>();
}
Currently, I have a race-condition issue where my application customizer is rendered before getting a response in my onInit() functions.
Soluzione
Batching the 2 requests as suggested by Venkat Konjeti is a great idea, but the question of how to delay your render until these are finished is demonstrated in the jquery-application-toastr sample (full disclosure, this is my sample).
Specifically in the SpfxToastrApplicationCustomizer.ts file. In the webpart class there is a private promise property. This is set in the onInit
function when the call to go retrieve the data is made. However, there is no handler (then
) at the time.
Later, in the onRender
method once things are ready, the then is finally applied and handled. This solves the race condition. Promises will allow you to apply the then
function before or after they are resolved. If they haven't resolved then your then
won't be called until it is, if the promise already completed, your then
will simply receive the results when you're ready for them.
Here's an example (borrowing the batch function from Venkat Konjeti's answer):
private myPromise: Promise<any>;
public onInit(): Promise<void> {
this.myPromise = this.getData()
}
public onRender(): void {
this.myPromise.then((results: any) => {
//Do some rendering stuff
});
}
function getData() {
let batch = $pnp.sp.createBatch();
$pnp.sp.web.select("Title").inBatch(batch).get().then(w => {
console.log(w.Title);
});
$pnp.sp.profiles.myProperties.inBatch(batch).get().then(result => {
var userProperties = result.UserProfileProperties;
console.log(userProperties);
userProperties.forEach(function (property) {
if (property.Key == "PreferredName") {
this._userDisplayName = property.Value;
console.log('user display name: ' + this._userDisplayName);
}
});
});
return batch.execute();
}
Altri suggerimenti
I think you can use the batch function. Here is example JavaScript code converted to the pnp batch executions.
function rednderBody() {
var promise = getMyWebDetails();
promise.then(() => console.log("All done!"));
}
function getMyWebDetails() {
var batch = $pnp.sp.createBatch();
$pnp.sp.web.select("Title").inBatch(batch).get().then(w => {
console.log(w.Title);
});
$pnp.sp.profiles.myProperties.inBatch(batch).get().then(result => {
var userProperties = result.UserProfileProperties;
console.log(userProperties);
userProperties.forEach(function (property) {
if (property.Key == "PreferredName") {
this._userDisplayName = property.Value;
console.log('user display name: ' + this._userDisplayName);
}
});
});
return batch.execute();
}
rednderBody();