gpt4 book ai didi

javascript - 如何检查 $compile 是否已经完成?

转载 作者:可可西里 更新时间:2023-11-01 02:25:59 24 4
gpt4 key购买 nike

我正在编写一个函数,可以根据 HTML 模板和给定的一些信息创建电子邮件模板。为此,我使用 Angular 的 $compile 函数。

只有一个问题我似乎无法解决。该模板由一个基本模板和无限量的 ng-include 组成。当我使用“最佳实践”$timeout ( advised here ) 时,它在我删除所有 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 函数).我相信这是因为 ng-include 的异步特性。


工作代码

此代码在完成渲染后返回 html 模板(现在可以重用函数,see this question for the problem)。但是这个解决方案是一个大问题,因为它使用 Angular 私有(private) $$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,并且只在成功创建模板时返回。我没有渲染这个模板,需要返回完全编译的模板。


解决方案

在尝试@estus 的回答后,我终于找到了另一种检查 $compile 何时完成的方法。这导致了下面的代码。我使用 $q.defer() 的原因是模板是在事件中解析的。因此,我无法像正常 promise 那样返回结果(我无法执行 return scope.$on())。这段代码中唯一的问题是它严重依赖于 ng-include。如果您为该函数提供一个没有 ng-include 的模板,则永远不会解析 $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;
}

最佳答案

$compile同步 函数。它只是同步编译给定的 DOM,并不关心嵌套指令中发生了什么。如果嵌套指令具有异步加载的模板或其他阻止其内容在同一时刻可用的东西,则这不是父指令的问题。

由于数据绑定(bind)和 Angular 编译器的工作方式,没有明显的时刻可以将 DOM 视为肯定“完整”,因为变化可能在任何地方、任何时间发生。 ng-include 也可能涉及绑定(bind),并且包含的​​模板可能随时更改和加载。

这里的实际问题是没有考虑以后如何管理的决定。使用随机模板的 ng-include 可以用于原型(prototype)制作,但会导致设计问题,这就是其中之一。

处理这种情况的一种方法是确定涉及哪些模板;设计良好的应用程序不能在其部分上过于松散。实际的解决方案取决于此模板的来源以及它包含随机嵌套模板的原因。但想法是,使用过的模板应该在使用前放入模板缓存中。这可以通过 gulp-angular-templates 等构建工具来完成。或者通过在 ng-include 编译之前使用 $templateRequest 进行请求(本质上是执行 $http 请求并将其放入 $templateCache ) - $templateRequest 基本上就是 ng-include 所做的。

虽然 $compile$templateRequest 在模板被缓存时是同步的,但是 ng-include 不是——它在下一次被完全编译打勾,即零延迟的 $timeout (a plunk ):

var templateUrls = ['foo.html', 'bar.html', 'baz.html'];

$q.all(templateUrls.map(templateUrl => $templateRequest(templateUrl)))
.then(templates => {
var fooElement = $compile('<div><ng-include src="\'foo.html\'"></ng-include></div>')($scope);

$timeout(() => {
console.log(fooElement.html());
})
});

通常,将模板用于缓存是消除 Angular 模板为编译生命周期带来的异步性的首选方法 - 不仅适用于 ng-include,而且适用于任何指令。

另一种方法是使用 ng-include events .这样,应用程序变得更加松散和基于事件(有时这是一件好事,但大多数时候不是)。由于每个 ng-include 都会发出一个事件,因此需要对事件进行计数,当它们被计数时,这意味着 ng-include 指令的层次结构已被完全编译(一个 plunk ):

var includeCounts = {};

var fooElement = $compile('<div><ng-include src="\'foo.html\'"></ng-include></div>')($scope);

$scope.$on('$includeContentRequested', (e, currentTemplateUrl) => {
includeCounts[currentTemplateUrl] = includeCounts[currentTemplateUrl] || 0;
includeCounts[currentTemplateUrl]++;
})
// should be done for $includeContentError as well
$scope.$on('$includeContentLoaded', (e, currentTemplateUrl) => {
includeCounts[currentTemplateUrl]--;

// wait for a nested template to begin a request
$timeout(() => {
var totalCount = Object.keys(includeCounts)
.map(templateUrl => includeCounts[templateUrl])
.reduce((counts, count) => counts + count);

if (!totalCount) {
console.log(fooElement.html());
}
});
})

请注意,这两个选项都只会处理由异步模板请求引起的异步性。

关于javascript - 如何检查 $compile 是否已经完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44492990/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com