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>

javascript

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 模态中的嵌入不起作用的主要内容,如果未能解决你的问题,请参考以下文章

范围绑定在模态弹出窗口angularjs中不起作用

模态表中的 UITextView 不起作用

Angular Bootstrap 模态弹出自动对焦不起作用

Angular-ui-bootstrap 手风琴和折叠动画不起作用

Angular DatePicker 的 Kendo UI 验证不起作用

Angular ui 路由器 - 重定向根本不起作用