I am creating a templateparser that can parse multiple templates and return the generated html content. For binding the html and view information I am using the Angular $compile
service. The problem I am encountering is that the promise .then() is called before the promise is resolved (and thus results in undefined
).
AngularJS version: 1.6.3
The parser function
/**
* Using the $compile function, this function generates a full HTML page based on the given process and template
* It does this by binding the given process to the template $scope and uses $compile to generate a HTML page
* @param {Afhandeling} process - The process that can bind to the template
* @param {string} templatePath - The location of the template that should be used
* @param {boolean} [useCtrlCall=true] - Whether or not the process should be a sub part of a $ctrl object. If the template is used
* for more then only an email template this could be the case (EXAMPLE: $ctrl.<process name>.timestamp)
* @return {IPromise<string>} A full HTML page
*/
public createEmail(process: Afhandeling, templatePath: string, useCtrlCall = true): ng.IPromise<string> {
let processScope = {};
if (useCtrlCall) { //Create scope object | Most templates are called with $ctrl.<process name>
const controller = "$ctrl";
processScope[controller] = {};
processScope[controller][process.__className.toLowerCase()] = process;
} else {
processScope = process;
}
return this.$http.get(templatePath)
.then((response) => {
let template = response.data;
let scope = this.$rootScope.$new();
angular.extend(scope, processScope);
let generatedTemplate = this.$compile(jQuery(template))(scope);
let waitForRenderCompletion = () => {
if (scope.$$phase || this.$http.pendingRequests.length) {
console.warn("Time for a timeout.");
this.$timeout(waitForRenderCompletion);
} else {
console.warn("Lets return the template.");
return generatedTemplate[0].innerHTML;
}
};
waitForRenderCompletion();
})
.catch((exception) => {
console.error(exception);
this.logger.error(
TemplateParser.getOnderdeel(process),
"Email template creation",
(<Error>exception).message
);
return null;
});
}
The function call
this.templateParser.createEmail(
this.model,
"<template url>"
).then((template: string) => {
console.warn(template); //Results in 'undefined'
});
The reason I am watchting $$phase
for changes is because of $compile
not giving any feedback on when it is done compiling. The template can consist of an undefinite number of templates bound together by ng-include
's. ng-includes are also async so I cannot think of any other way to check when the $compile
is done (My question about a better solution then this).
What I am thinking
When I look at the console output I get the following:
Time for a timeout.
undefined
(2) Time for a timeout.
Lets return the template.
So it seems like the promise is automatically resolved when the first $timeout
resolves. Yet this doesn't make any sense, since I am not returning anything.
Any help is appreciated.
Answer
Thanks @charlietfl for the hint. The working code is below. I'm now returning the function, so that I have a return value in my promise. I am also returning the $timeout
, so that the function can be called recursively.
The code:
let waitForRenderCompletion = () => {
if (scope.$$phase || this.$http.pendingRequests.length) {
console.warn("Time for a timeout.");
return this.$timeout(waitForRenderCompletion);
});
} else {
console.warn("Lets return the template.");
return generatedTemplate[0].innerHTML;
}
};
return waitForRenderCompletion();