Angularjs 在指令中包装输入

Angularjs 在指令中包装输入,angularjs,angularjs-directive,angular-ui-bootstrap,Angularjs,Angularjs Directive,Angular Ui Bootstrap,我的想法是将输入打包成自定义指令,以确保整个站点的外观和行为一致。我还想包装引导ui的日期选择器和下拉列表。此外,该指令还应处理验证和显示工具提示 HTML应该如下所示: <my-input required max-length='5' model='text' placeholder='text' name='text'/> 或 在指令中,我想创建一个dom结构,如: <div> <div>..</div> //display va

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

HTML应该如下所示:

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


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

<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>

.. //在此处显示验证
.. //添加按钮切换日期选择器(或其他东西)在这里
.. //在此处添加输入字段
我尝试了各种方法来实现这一点,但总是遇到一些权衡:

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

  • 仅使用“替换”。在本例中,所有属性都应用于最外层的div(即使我在compile函数中生成了模板dom结构)。如果我只使用输入作为模板,那么属性就在输入上,但是我需要在link函数中生成模板,然后重新编译它。就我对angular的阶段模型的理解而言,我希望避免在link函数中重新编译和更改模板dom(尽管我见过很多人这样做)


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

    为什么不这样做呢

    myApp.directive('wrapForm', function(){
        return {
            restrict: 'AC',
            link: function(scope, inputElement, attributes){                       
                var overallWrap = angular.element('<div />');
                var validation = angular.element('<div />').appendTo(overallWrap);
                var button = angular.element('<div />').appendTo(overallWrap);
                var inputWrap = angular.element('<div />').appendTo(overallWrap);
    
                overallWrap.insertBefore(inputElement);
                inputElement.appendTo(inputWrap);
    
                inputElement.on('keyup', function(){
                    if (inputElement.val()) {
                        validation.text('Just empty fields are valid!');
                    } else {
                        validation.text('');
                    }
                });            
            }
        }
    });
    
    myApp.directive('wrapForm',function(){
    返回{
    限制:“AC”,
    链接:函数(作用域、inputElement、属性){
    var overallWrap=角度元素(“”);
    var验证=角度元素(“”).appendTo(整体包裹);
    var按钮=角度元素(“”).appendTo(整体包裹);
    var inputWrap=angular.element(“”).appendTo(overallWrap);
    整体包裹。插入前(inputElement);
    inputElement.appendTo(inputWrap);
    inputElement.on('keyup',function()){
    if(inputElement.val()){
    text('只有空字段有效!');
    }否则{
    验证。文本(“”);
    }
    });            
    }
    }
    });
    
    小提琴:

    基本上,您使用原始输入字段(顺便说一句,它是)并分别构建包装。在本例中,我只是手动构建div。对于更复杂的内容,您还可以使用angularjs获取
    $compile
    (d)的模板


    使用这个类或html属性“wrapForm”的好处是:您可以对几种表单输入类型使用相同的指令。

    以下是我认为正确的方法。像OP一样,我希望能够使用attribute指令包装
    输入
    。但我也希望它能够在不泄漏任何元素的情况下使用
    ngif
    等。正如@jantimon所指出的,如果不清理包装器元素,那么如果破坏了原始元素,它们将在ng之后徘徊

    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();
            });
          }
        };
      }
    ]);
    
    app.directive(“checkboxWrapper”,[function(){
    返回{
    限制:“A”,
    链接:函数(范围、元素、属性、ctrl、transclude){
    var wrapper=angular.element('此输入已包装');
    元素。之后(包装器);
    wrapper.prepend(元素);
    作用域:$on(“$destroy”,函数(){
    包装器。在(元素)之后;
    wrapper.remove();
    });
    }
    };
    }
    ]);
    
    你可以和我一起玩


    重要
    范围
    vs
    元素
    $destroy。您必须将清理放在
    范围内(“$destroy”)
    ,而不是
    元素中(“$destroy”)
    (这是我最初尝试的)。如果在后一个元素中执行此操作,则“ngIf end”注释标记将被泄漏。这是由于Angular的ngIf在执行错误逻辑时如何清理其结束注释标记。通过将指令的清理代码放在$destroy范围内,您可以像包装输入之前一样将DOM放回原处,因此ng if的清理代码是令人满意的。在调用element.on(“$destroy”)时,如果错误地打开原始元素而不导致注释标记泄漏,则ng if flow中的时间就太晚了。

    为什么不在编译函数中包装输入? 优点是不必复制属性,也不必在scopedestroy函数中进行清理。 请注意,您必须删除directive属性以防止循环执行

    ()

    angular.module('directions').directive('wrappedWithDiv'[
    函数(){
    变量定义={
    限制:“A”,
    编译:函数(元素、属性){
    元素。removeAttr(“用div包裹”);
    元素。替换为(“”)+
    元素[0]。outerHTML+“”)
    }
    }
    返回定义;
    }
    ]);
    
    基于此:

    此指令执行转置,但转置的内容使用父范围,因此所有绑定都像转置的内容在使用包装器的原始范围内一样工作。这当然包括ng模型、最小值/最大值和其他验证指令/属性。应该适用于任何内容。我没有使用ng TRANCLUDE指令,因为我正在手动克隆元素并为它们提供父(控制器)作用域。“我的幽魂”
    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();
            });
          }
        };
      }
    ]);
    
    angular.module('directives').directive('wrappedWithDiv', [
        function() {
            var definition = {
                restrict: 'A',
                compile: function(element, attrs) {
                    element.removeAttr("wrapped-with-div");
                    element.replaceWith("<div style='border:2px solid blue'>" +
                        element[0].outerHTML + "</div>")
                }
            }
            return definition;
        }
    ]);
    
        controlsModule.directive('myWrapper', function () {
            return {
                restrict: 'E',
                transclude: true,
                scope: {
                    label: '@',
                    labelClass: '@',
                    hint: '@'
                },
    
                link: link,
                template:
                    '<div class="form-group" title="{{hint}}"> \
                        <label class="{{labelClass}} control-label">{{label}}</label> \
                        <my-transclude></my-transclude> \
                     </div>'
            };
    
            function link(scope, iElement, iAttrs, ctrl, transclude) {
    
                transclude(scope.$parent,
                    function (clone, scope) {
    
                        iElement.find("my-transclude").replaceWith(clone);
    
                        scope.$on("$destroy", function () {
                            clone.remove();
                        });
                    });
            }
        });