Angular UI 模态中的嵌入不起作用
Posted
技术标签:
【中文标题】Angular UI 模态中的嵌入不起作用【英文标题】:Transclusion in Angular UI Modal not working 【发布时间】:2017-02-05 16:09:45 【问题描述】:this plunk 的目标是将元素从控制器转换为 Angular UI 模态,其中模态由指令包装。解决方案应遵循以下前提:
该指令声明了字段的嵌入。这些字段包含在控制器 html 标记的指令声明中。 控制器中声明的这些字段应显示在 Modal 中。 这些字段的范围应该可以在控制器中访问(请参阅我在控制器中声明了一个input1
变量,该变量应该在 Modal 中设置一个值)。
我定义了一个content
元素来嵌入字段。这个元素在模态的模板中。我不确定这个模板什么时候可以嵌入它。
总而言之,目标是在控制器 HTML 标记中声明一组字段并在模态中可用,其中模态被包装在指令中,范围在控制器中管理。任何想法将不胜感激。
HTML
<div the-modal control="modalCtl">
<p>some text</p>
<input type="text" ng-model="input1" />
</div>
<button type="button" ng-click="open()">Open me!</button>
var app = angular.module("app", ['ui.bootstrap']);
app.controller("ctl", function($scope,$timeout)
$scope.modalCtl = ;
$scope.input1 = "abc";
$scope.open = function()
$scope.modalCtl.openModal();
;
);
app.directive("theModal", function($uibModal)
return
restrict: "AE",
scope:
control: "="
,
transclude: true,
link: function (scope, element, attrs, ctrl, transclude)
scope.control = scope.control ||
scope.control.openModal = function ()
scope.instance = $uibModal.open(
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>'
);
element.find('.content').append(transclude());
;
);
【问题讨论】:
您正试图在模态模板被激活并插入 dom 之前附加到模态模板。快速测试...console.log(element.find('.content').length)//0
谢谢,我在模态框创建后立即更改了element.find('.content').append(transclude());
的位置,但仍然一无所获
但模态不是该元素的一部分...看看它在 dom 中的插入位置(检查浏览器开发工具中的实时 html)
【参考方案1】:
您已经非常接近通过嵌入实现您的目标,但是,您需要考虑一些事项:
首先,根据UI Bootstrap docs,options
中有一个appendTo
属性,用于$uibModal.open()
方法默认为body
。
如果未指定
appendTo
,模式将附加到您页面的body
并成为body
的直接子级。因此,通过element.find('.content')
在指令中查询.content
将不起作用,因为它不存在。
其次,AngularJS 带有 jQLite,这是 jQuery 的轻量级版本。这意味着对大多数 jQuery 功能的支持有限。一种这样的情况是 .find()
方法,它只适用于标签名称。
要使其与 jQuery 一样工作(虽然您实际上不必这样做,因为您仍然可以在链中使用 .children()
来查询嵌套的 DOM 元素),您将拥有在 Angular 之前加载 jQuery(我想你已经)。
更多信息请参考AngularJS docs on angular.element
。
渲染 DOM 对 Angular 来说需要一点时间,因为它需要进行与范围和视图相关的正确绑定,完成一个摘要循环等等。 因此,您最终可能会立即查询实际上可能尚未呈现的 DOM 元素。
等待 DOM 渲染和摘要循环完成的技巧是将与 DOM 相关的代码包装到
$timeout
包装器中。
考虑到以上几点,您的自定义指令theModal
的链接函数中的openModal
方法应如下所示:
scope.control.openModal = function ()
scope.instance = $uibModal.open(
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
/**
* Make sure the modal is appended to your directive and NOT `body`
*/
appendTo: element
);
/**
* Give Angular some time to render your DOM
*/
$timeout(function ()
/**
* In case jQuery is not available
*/
// var content = element.children('.modal').children('.modal-dialog').children('.modal-content').children('.content');
/**
* Since you have jQuery loaded already
*/
var content = element.find('.content');
/**
* Finally, append the transcluded element to the correct position,
* while also making sure that the cloned DOM is bound to the parent scope (i.e. ctl)
*/
transclude(scope.$parent, function(clonedContent)
content.append(clonedContent);
);
);
;
注意transclude
函数如何让您控制您希望如何将一些转入的 DOM 绑定到自定义范围,而不是默认指令的范围。普通的 transclude()
调用将考虑当前可用的范围对象 - 即指令的范围 - 用于绑定转入的 DOM。
Demo
【讨论】:
这可行,但是当模态中的元素数量很大时,呈现模态时会出现明显的延迟,有什么办法可以解决这个问题吗?不应与$timeout
相关,因为它是零秒。
@ps0604,你能把 plunk 分叉出来让我看到问题吗?
我做了一个 plunker here,它生成 900
输入元素,所有元素都被嵌入到模态并绑定到控制器中的 900
值;令人惊讶的是,几乎所有时间打开模态都需要不到一秒钟的时间。我想您正在使用 templateUrl
而不是 template
来保持分开。如果是这种情况,那么在从 templateUrl
解析数据之前,模态内的 DOM 将不会被渲染 - 这需要太长时间。
你的解决方案很好,我可能在其他地方有问题【参考方案2】:
正如前面的答案所暗示的,您可以使用appendTo
属性将指令的元素提供为模态的父级。
您可以使用 UibModalIstance 中的rendered
承诺“等待模态模板被呈现”。 (Documentation)。
scope.control.openModal = function ()
scope.instance = $uibModal.open(
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
appendTo: element
);
// We use the redered promise to make sure
// the modal's template has been loaded.
scope.instance.rendered.then(function ()
// You'll most likely want to pass the `$parent` scope as the first
// parameter for proper scope binding with your controller.
element.find('.content').append(transclude(scope.$parent));
);
;
这是modified plunker.
【讨论】:
我收到了scope.instance.rendered is not a function
,有什么想法吗?
嘿@ps0604,在我提供的分叉 plunker 中它工作正常。也许你正在测试不同版本的 Anuglar UI?文档指出open
方法返回一个包含rendered
承诺的对象。
嘿@ps0604,我已经意识到我在答案中编写的代码中有一个错误,但在 Plunker 中没有,因此 plunker 正在工作,但不是我提供的代码。如果您愿意再次检查,请使用:scope.instance.rendered.then(function())
【参考方案3】:
transclude: true,
不是这样工作的,它将插入在指令范围内定义的任何标记,并将该标记放入指令模板中(您将在其中放置 ngTransclude
)。这不是您(似乎)试图做的事情。
您正在寻找的是定义一个模板,给它一个 url 并使用 templateUrl
属性将其提供给模态。
HTML
<script type="text/ng-template" id="/some-tpl.html">
<p>some text</p>
<input type="text" value="1234" />
</script>
JS
$uibModal.open(
animation: false,
scope: scope,
templateUrl: "/some-tpl.html" // link to template url
)
然后你可以将你的指令/你的逻辑放在你提供模式的控制器中。
Here is an updated plunk
【讨论】:
哎呀,我的错,修好了! 为什么要在指令中定义transclude
?从您的回答看来,它不应该存在。
我没有嵌入,因为它不是必需的。我只是为模态提供一个模板。我只是为了说明一点而改变了你的笨拙,我应该删除它。
我从您的示例中删除了 transclude,但它不起作用
我知道,将$uibModal.open
例程放在控制器中,然后将指令放在模态模板中,然后您将看到要嵌入的标记。如果我太模棱两可,我可以更新 plunk以上是关于Angular UI 模态中的嵌入不起作用的主要内容,如果未能解决你的问题,请参考以下文章
Angular Bootstrap 模态弹出自动对焦不起作用
Angular-ui-bootstrap 手风琴和折叠动画不起作用