在不阻塞 dom 的情况下从后端渲染 ($compile) html 到视图中
Posted
技术标签:
【中文标题】在不阻塞 dom 的情况下从后端渲染 ($compile) html 到视图中【英文标题】:Render ($compile) html from backend into view without blocking dom 【发布时间】:2017-04-09 04:25:57 【问题描述】:上下文
我需要在我的 AngularJS (v1.4) 应用程序中加载一些从后端获取的 html,并将其(html)插入到我的 partial(已经加载)。部分已经加载了一些 HTML(并且功能齐全)。现在,我可以加载 HTML 并使用此处发布的指令 (Compiling dynamic HTML strings from database) 对其进行编译。请参阅下面的代码。
问题
但是...当部分 HTML 已加载(部分已加载且功能正常),然后我从后端获取另一个 HTML 内容,并且指令正在编译该新内容,即整个文档(DOM) 被“冻结”。我无法输入输入或单击按钮,包括我之前加载的 HTML 中的按钮。
问题
我如何加载 HTML 内容,$compile
在“背景”或任何其他允许我继续使用其余(已经正常工作的)HTML 的方式?
对我来说,必须编译到达的新 html 内容,因为它包含角度验证等需要编译并进入“角度世界”(在角度digest cycle 等)。
这是我用于编译 html 的指令
(function ()
var dynamic = function($compile)
return
restrict: 'A',
replace: true,
link: function (scope, ele, attrs)
scope.$watch(attrs.dynamic, function(html)
if (html)
ele.html(html);
$compile(ele.contents())(scope);
);
;
;
dynamic.$inject = ['$compile'];
angular.module('app')
.directive('dynamic', dynamic);
());
在控制器中我有类似的东西
// this will be filled with asynchronous calls were I get the HTMLs from a service
// in order to keep this example simple I just made a demo, not with the real async calls
$scope.secciones = []
//when the promises are getting resolved "secciones" would be something like (more items can be added after in time)
$scope.secciones = [
html: "<div> some html content here (not too small sometimes) </div>",
html: "<div> another html content here (not too small sometimes) </div>"
]
...在视图中
<!--every time an async call with html is resolved, it's added to secciones, then a new div is generated and compiled-->
<!-- if there was some html previously rendered and the app starts compiling new html the UI gets "freezed"-->
<div ng-repeat="item in secciones">
<div dynamic="item.html"></div>
</div>
注意:我使用这种方法是因为每个 html 代表我拥有的选项卡面板中的一个选项卡,其中用户实际上在“secciones”(其他是隐藏的,但仍然存在),但我需要编译其他的,以便在用户单击其他选项卡时让它们为用户做好准备(secciones 中的另一个 html)。
如果可以通过升级到更新版本的 AngularJS(1.x) 来解决这个问题,比如说 1.6。我很乐意尝试一下。
【问题讨论】:
使用$timeout
包装模板的编译。这样你就不会阻塞主线程并且你的应用会响应
还有$scope.$evalAsync。参考:docs.angularjs.org/api/ng/type/$rootScope.Scope
谢谢大家。使用我在问题中提到的指令,我做了`scope.$evalAsync(function()ele.html(html);$compile(ele.contents())(scope);); ` 和 ` $timeout(function () ele.html(html); $compile(ele.contents())(scope); ) ` 行为是一样的 :(
@AsielLealCeldeiro 也许您可以将指令的代码添加到我为了找到正确的解决方案的问题中
@Kliment 我添加了我最初在问题中提到的指令的代码以及我正在做的一个例子。希望它有助于澄清情况。任何其他问题,请随时提出。非常感谢您的帮助!
【参考方案1】:
您可以尝试使用该指令来编译 HTML 代码。该指令在检测变量htmlCode
的任何变化时编译代码
module.directive('bindHtmlCompile', ['$compile', function ($compile)
return
restrict: 'A',
link: function (scope, element, attrs)
scope.$watch(function ()
return scope.$eval(attrs.bindHtmlCompile);
, function (value)
element.html(value && value.toString());
var compileScope = scope;
if (attrs.bindHtmlScope)
compileScope = scope.$eval(attrs.bindHtmlScope);
$compile(element.contents())(compileScope);
);
;
]);
你可以通过凉亭安装,DirectiveRepo
用法:
<div bind-html-compile="htmlCode"></div>
【讨论】:
感谢@Isma90 的帮助。我刚刚尝试了您的指令,但最终结果是相同的。当 html 有点大时,屏幕会像以前一样冻结。【参考方案2】:基本上我是通过从脚本标签中获取 html 并编译它并附加到现有的 div 来完成的。 你可以使用下面的sn-p。
在你的 html 中包含 div
<div id="step-container">
</div>
控制器代码
var template = $templateCache.get('basicInfo'); // or your html
$compile($("#step-container").html(template).contents())($scope);
$("#step-container").show();
为了演示,这将包含在 html 页面中
<script type="text/ng-template" id="basicInfo"></script>
通过这种方式,您可以编译来自后端的 html。
希望对你有帮助。
【讨论】:
一旦我试一试,我会给你一些反馈。非常感谢您的帮助! 我一直在玩你的解决方案,但我想我误解了 $templateCache.get 方法的作用。我虽然它会接收一些 html 内容(包括字符串 html),但似乎只有 htmll 内容,在一个文件中,例如它可以传递给该方法。我正在传递我要呈现的 html 的字符串表示,但 $templateCache.get 方法返回未定义,请参阅我的 devtools 的截图。 “data”是html字符串,template是$templateCache.get的返回请看dropbox.com/s/5k2m3gs56grxoqp/sample.png?dl=0 您不必使用模板缓存,只需将您的 html 作为字符串传递。我已经评论了 // 或您的 html // 那里,例如 var template = " " 或者当您从后端获取 html 时,将其添加到模板缓存中,然后从中接收跨度> 非常感谢!但我无法在不影响页面可用性的情况下完成这项工作。我的问题的一个关键点是,当一些(新的)html 被 angular 编译时,它不会阻塞页面的其余部分,并且可以“顺利”地“添加”到 DOM 内容中。根据docs.angularjs.org/api/ng/service/$templateCache,我使用了另一种变体,并尝试执行$templateCache.put("myId" ,data);
,其中 data 是我要渲染的新html,在html中我放了<div data-ng-include="myId"></div>
但没有运气, tihs 的行为方式相同
该解决方案实际上是编译我的html并将其放入页面,但该过程仍然冻结网页。 $("#step-container").show();
是否打算仅在新内容准备好时“整合”新内容?也许我使用不正确。再次感谢您的帮助!以上是关于在不阻塞 dom 的情况下从后端渲染 ($compile) html 到视图中的主要内容,如果未能解决你的问题,请参考以下文章
如何在不渲染的情况下从 forwardref 组件添加 ref?
如何在不阻塞的情况下从 Spring Webflux 中的 Mono 对象中提取数据?