我正在编写一个函数,可以根据 HTML 模板和给出的一些信息创建电子邮件模板。为此,我正在使用$compile
角的函数。
只有一个问题我似乎无法解决。该模板由一个基本模板和无限数量的ng-include
的。当我使用“最佳实践”时$timeout
(在这里建议 https://stackoverflow.com/questions/17505484/why-is-the-content-of-an-ng-include-dynamically-loaded-template-not-available-vi)当我删除所有的ng-include
的。所以这不是我想要的。
$超时示例:
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);
return this.$timeout(() => {
return generatedTemplate[0].innerHTML;
});
})
.catch((exception) => {
this.logger.error(
TemplateParser.getOnderdeel(process),
"Email template creation",
(<Error>exception).message
);
return null;
});
当我开始添加时ng-include
到模板,此函数开始返回尚未完全编译的模板(解决方法是嵌套$timeout
功能)。我相信这是因为 a 的异步性质ng-include
.
工作代码
此代码在渲染完成后返回 html 模板(函数现在可以重用,看到这个问题的问题 https://stackoverflow.com/questions/44541084/recursive-function-in-promise-with-timeout-resolves-to-quick)。但这个解决方案是一个很大的失败,因为它使用的是角度私有$$phase
检查是否有任何正在进行的$digest
的。所以我想知道是否还有其他解决方案?
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 waitForRenderAndPrint = () => {
if (scope.$$phase || this.$http.pendingRequests.length) {
return this.$timeout(waitForRenderAndPrint);
} else {
return generatedTemplate[0].innerHTML;
}
};
return waitForRenderAndPrint();
})
.catch((exception) => {
this.logger.error(
TemplateParser.getOnderdeel(process),
"Email template creation",
(<Error>exception).message
);
return null;
});
我想要的是
我想要一个可以处理无限数量的功能ng-inlude
并仅在模板创建成功时返回。我不渲染此模板,需要返回完全编译的模板。
Solution
在尝试了 @estus 答案之后,我终于找到了另一种检查 $compile 何时完成的方法。这导致了下面的代码。我使用的原因$q.defer()
这是因为模板在事件中被解析。因此,我无法像正常承诺一样返回结果(我不能做return scope.$on()
)。这段代码中唯一的问题是它严重依赖于ng-include
。如果您提供的函数模板没有ng-include
the $q.defer
从未得到解决。
/**
* 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 {Process} process - The data 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 parseHTMLTemplate(process: Process, templatePath: string, useCtrlCall = true): ng.IPromise<string> {
let scope = this.$rootScope.$new(); //Do NOT use angular.extend. This breaks the events
if (useCtrlCall) {
const controller = "$ctrl"; //Create scope object | Most templates are called with $ctrl.<process name>
scope[controller] = {};
scope[controller][process.__className.toLowerCase()] = process;
} else {
scope[process.__className.toLowerCase()] = process;
}
let defer = this.$q.defer(); //use defer since events cannot be returned as promises
this.$http.get(templatePath)
.then((response) => {
let template = response.data;
let includeCounts = {};
let generatedTemplate = this.$compile(jQuery(template))(scope); //Compile the template
scope.$on('$includeContentRequested', (e, currentTemplateUrl) => {
includeCounts[currentTemplateUrl] = includeCounts[currentTemplateUrl] || 0;
includeCounts[currentTemplateUrl]++; //On request add "template is loading" indicator
});
scope.$on('$includeContentLoaded', (e, currentTemplateUrl) => {
includeCounts[currentTemplateUrl]--; //On load remove the "template is loading" indicator
//Wait for the Angular bindings to be resolved
this.$timeout(() => {
let totalCount = Object.keys(includeCounts) //Count the number of templates that are still loading/requested
.map(templateUrl => includeCounts[templateUrl])
.reduce((counts, count) => counts + count);
if (!totalCount) { //If no requests are left the template compiling is done.
defer.resolve(generatedTemplate.html());
}
});
});
})
.catch((exception) => {
defer.reject(exception);
});
return defer.promise;
}