Sorry for the essay, couldn't find a short way to explain this :(
Context
I am currently building a WebPart for Office 365 which is pretty much like a client side App that users can add to their page and configure through a UI.
The WebPart renders some content, and I would like users to be able to provide an external script that could run AFTER the rendering process. So far this is easy, I could simply do something like this in the WebPart code :
// Render the WebPart
this.render();
// Load external script
this.loadExternalScript(this.props.externalScriptUrl);
The issue is I would like the external script to act like a callback, that my WebPart could call in order to provide some context to it.
Solution 1
The first solution that I found, was to provide guidance for the users on how to create their external script using a specific namespace based on the file name, and a precise function so that way my WebPart could :
That works well and it looks like this :
MyExternalScript.js
MyNamespace.ExternalScripts.MyExternalScript = {
onPostRender: function(wpContext) {
console.log(wpContext);
}
}
WebPart
// Render the WebPart
this.render();
// Load external script
this.loadExternalScript(this.props.externalScriptUrl);
// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace ? scriptNamespace.onPostRender : null;
if(scriptCallback) {
scriptCallback(this.wpContext);
}
That works fine but building the namespace based on the file's name is a pretty sketchy thing to ask users to do, and I would love to find something more straight forward than this hacky solution.
Solution 2
Another solution I thought about was to do the following :
That would look along these lines :
MyExternalScript.js
onPostRender: function(wpContext) {
console.log(wpContext);
}
WebPart
// Render the WebPart
this.render();
// Load external script content
$scriptContent = this.readExternalScript(this.props.externalScriptUrl);
// Append unique namespace
$scriptContent = "MyNamespace.ExternalScripts.MyExternalScript = {" + $scriptContent + "}";
// Eval everything within that namespace
eval($scriptContent);
// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace ? scriptNamespace.onPostRender : null;
if(scriptCallback) {
scriptCallback(this.wpContext);
}
I did some quick testing and that looks like it's working, the fact that the WebPart dynamically generates the namespace is way better than asking the user to comply to a complicated namespace, however I am not sure if there is a better solution than using eval().
All I need at the end of the day is to find a way to make my WebPart "aware" of the callback it needs to call. I also have to make sure that the namespacing is WebPart-unique and script-unique as there could be 4 WebParts on the same page, loading different scripts, so I have to avoid namespace conflicts at all costs.
Anyone have a better idea?
Thanks!
I don't quite understand the context here but how about passing in the post render function as a callback?
var runExternalScript = Function('onPostRender', `
// your external script
console.log('rendering...');
console.log('rendering finished');
if (onPostRender) onPostRender();
`)
function postCallback(){ console.log('finished!') }
runExternalScript(postCallback)