本文介绍了在 angular 指令中包装输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的想法是将输入包装到自定义指令中,以保证整个网站的外观和行为一致.我还想包装 bootstrap ui 的日期选择器和下拉列表.此外,该指令还应处理验证和显示工具提示.

HTML 应如下所示:

在指令中我想创建一个 dom 结构,如:

<div>..</div>//在这里显示验证<div>..</div>//在这里添加按钮来切换日期选择器(或其他东西)<div>..</div>//在这里添加输入框

我尝试了各种方法来实现这一点,但总是遇到一些权衡:

  1. 使用 transclude 和 replace 将输入插入到指令 dom 结构中(在这种情况下,指令将被限制为 'A' 而不是 'E',如上例所示).这里的问题是,没有简单的方法来访问被嵌入的元素,因为我想在 datepicker 的情况下添加自定义属性.我可以使用 transclude 函数,然后在链接函数中重新编译模板,但这对于这个任务来说似乎有点复杂.这也会导致日期选择器的嵌入范围和切换状态出现问题(一个在指令范围内,另一个在嵌入范围内).

  2. 仅使用替换.在这种情况下,所有属性都应用于最外层的 div(即使我在 compile 函数中生成模板 dom 结构).如果我只使用输入作为模板,那么属性在输入上,但我需要在链接函数中生成模板,然后重新编译它.就我对angular的阶段模型的理解,我想避免重新编译和更改link函数中的模板dom(虽然我已经看到很多人这样做了).

目前我正在使用第二种方法并在链接函数中生成模板,但我想知道是否有人有更好的想法!

解决方案

这是我认为正确的方法.像 OP 一样,我希望能够使用属性指令来包装 input.但我也希望它与 ng-if 等一起工作而不会泄漏任何元素.正如@jantimon 指出的那样,如果你不清理你的包装元素,它们会在 ng-if 破坏原始元素后逗留.

app.directive("checkboxWrapper", [function() {返回 {限制:A",链接:函数(范围,元素,属性,ctrl,transclude){var wrapper = angular.element('<div class="wrapper">这个输入被包装了</div>');element.after(包装器);wrapper.prepend(element);scope.$on("$destroy", function() {包装后(元素);包装器移除();});}};}]);

还有 这里有一个 plunker 你可以玩.

重要:scope vs element $destroy.您必须将清理放在 scope.$on("$destroy") 而不是 element.on("$destroy") (这是我最初尝试的).如果你在后者(元素)中这样做,那么ngIf end"注释标签将被泄露.这是由于 Angular 的 ngIf 在执行其虚假逻辑时如何清理其结束注释标签.通过将指令的清理代码放在作用域 $destroy 中,您可以将 DOM 放回包装输入之前的状态,因此 ng-if 的清理代码很高兴.到 element.on("$destroy") 被调用时,在 ng-if falsey 流中打开原始元素而不会导致注释标记泄漏为时已晚.

I had the idea to wrap inputs into custom directives to guarantee a consistent look and behavior through out my site. I also want to wrap bootstrap ui's datepicker and dropdown. Also, the directive should handle validation and display tooltips.

The HTML should look something like this:

<my-input required max-length='5' model='text' placeholder='text' name='text'/>

or

<my-datepicker required model='start' placeholder='start' name='start'/>

in the directives i want to create a dom structure like:

<div>
 <div>..</div> //display validation in here
 <div>..</div> //add button to toggle datepicker (or other stuff) in here
 <div>..</div> //add input field in here
</div>

I tried various ways to achieve this but always came across some tradeoffs:

  1. using transclude and replace to insert the input into the directives dom structure (in this case the directive would be restricted to 'A' not 'E' like in the example above). The problem here is, that there is no easy way to access the transcluded element as I want to add custom attributes in case of datepicker. I could use the transclude function and then recompile the template in the link function, but this seems a bit complex for this task. This also leads to problems with the transcluded scope and the toggle state for the datepicker (one is in the directives scope, the other in the transcluded scope).

  2. using replace only. In this case, all attributes are applied to the outermost div (even if I generate the template dom structure in the compile function). If I use just the input as template, then the attributes are on the input, but I need to generate the template in the link function an then recompile it. As far as I understand the phase model of angular, I would like to avoid recompiling and changing the template dom in the link function (although I've seen many people doing this).

Currently I'm working with the second approach and generating the template in the link function, but I was wondering if someone had some better ideas!

解决方案

Here's what I believe is the proper way to do this. Like the OP I wanted to be able to use an attribute directive to wrapper an input. But I also wanted it to work with ng-if and such without leaking any elements. As @jantimon pointed out, if you don't cleanup your wrapper elements they will linger after ng-if destroys the original element.

app.directive("checkboxWrapper", [function() {
    return {
      restrict: "A",
      link: function(scope, element, attrs, ctrl, transclude) {
        var wrapper = angular.element('<div class="wrapper">This input is wrappered</div>');

        element.after(wrapper);
        wrapper.prepend(element);

        scope.$on("$destroy", function() {
          wrapper.after(element);
          wrapper.remove();
        });
      }
    };
  }
]);

And here's a plunker you can play with.

IMPORTANT: scope vs element $destroy. You must put your cleanup in scope.$on("$destroy") and not in element.on("$destroy") (which is what I was originally attempting). If you do it in the latter (element) then an "ngIf end" comment tag will get leaked. This is due to how Angular's ngIf goes about cleaning up its end comment tag when it does its falsey logic. By putting your directive's cleanup code in the scope $destroy you can put the DOM back like it was before you wrappered the input and so ng-if's cleanup code is happy. By the time element.on("$destroy") is called, it is too late in the ng-if falsey flow to unwrap the original element without causing a comment tag leak.

这篇关于在 angular 指令中包装输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 01:28