【问题标题】:wrapping inputs in directives in angular以角度包装指令中的输入
【发布时间】:2014-01-06 08:01:14
【问题描述】:

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

HTML 应该如下所示:

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

或

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

在指令中我想创建一个 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>

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

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

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

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

【问题讨论】:

  • 您希望能够添加什么样的自定义属性?我认为,您希望指令呈现到的 HTML 的完整示例(自定义属性和所有)在这里会很有用。
  • 在日期选择器的情况下,我想设置应用程序范围的标准值。生成的 html 输入标记应如下所示:&lt;input type="text" data-datepicker-popup="" data-is-open="opened" data-current-text="{{current}}" data-close-text="{{text}}" data-clear-text="{{clear}}" data-show-weeks="{{showweeks}}" data-starting-day="{{startingday}}" placeholder="{{placeholder}}" data-ng-model="start" name="start" required="required" class="ng-isolate-scope ng-pristine ng-valid ng-valid-required"&gt;

标签: angularjs angularjs-directive angular-ui-bootstrap


【解决方案1】:

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

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();
        });
      }
    };
  }
]);

还有here's a plunker你可以玩。

重要提示:scope 与 element $destroy。你必须把你的清理放在scope.$on("$destroy")而不是element.on("$destroy")(这是我最初尝试的)。如果您在后者(元素)中执行此操作,则“ngIf end”注释标记将被泄露。这是由于 Angular 的 ngIf 在执行错误逻辑时如何清理其结束注释标记。通过将指令的清理代码放在 $destroy 范围内,您可以将 DOM 恢复为包装输入之前的状态,因此 ng-if 的清理代码很高兴。到 element.on("$destroy") 被调用时,在 ng-if falsey 流程中已经太晚了,无法解开原始元素而不会导致注释标签泄漏。

【讨论】:

  • Angular's jqLite has wrap。为什么不使用 after 和 prepend 呢?
  • 尝试wrap 给我带来了一些困难。当然,它会是更简洁/更简洁的语法,但由于某种原因,使用wrap 时清理不起作用。在 Plunker 中尝试wrap,然后切换 Show 复选框(切换 ngIf 条件)。我承认我没有进一步调查,但如果有人想进一步调查,我对细节很感兴趣。
【解决方案2】:

为什么不做这样的指令?

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('');
                }
            });            
        }
    }
});

小提琴:http://jsfiddle.net/bZ6WL/

基本上,您采用原始输入字段(顺便说一下,also an angularjs directive)并单独构建包装。在此示例中,我只是手动构建 DIV。对于更复杂的东西,您还可以使用通过 angularjs 获取 $compile(d) 的模板。

使用此类或 html 属性“wrapForm”的优势:您可以对多种表单输入类型使用相同的指令。

【讨论】:

  • 我在小提琴中看到过这个,但这对我不起作用,不知道为什么。我的应​​用程序中有 jquery-1.10.2 和 angular.js,但仍然不起作用! !
  • 小心 - 如果你将它与 ng 一起使用 - 如果它不会清除包装器!
  • 这是一个很好的建议,但 Angular 没有 appendTo 或 insertBefore。可以使用 Append 和 after(分别)来实现类似的结果。
【解决方案3】:

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

(http://jsfiddle.net/oscott9/8er3fu0r/)

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;
    }
]);

【讨论】:

    【解决方案4】:

    基于此:http://angular-tips.com/blog/2014/03/transclusion-and-scopes/

    该指令进行嵌入,但被嵌入的东西使用父范围,因此所有绑定都像被嵌入的内容在使用包装器的原始范围内一样工作。这当然包括 ng-model、min/max 和其他验证指令/属性。应该适用于任何内容。我没有使用 ng-transclude 指令,因为我正在手动克隆元素并向它们提供父(控制器)范围。使用“my-transclude”而不是 ng-transclude 来指定在哪里插入被嵌入的内容。

    太糟糕了 ng-transclude 没有控制范围的设置。这将使所有这些笨重的东西变得不必要。 看起来他们不会修复它:https://github.com/angular/angular.js/issues/5489

        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();
                        });
                    });
            }
        });
    

    【讨论】:

      猜你喜欢
      • 2015-03-04
      • 2019-05-06
      • 2020-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      • 2018-02-14
      相关资源
      最近更新 更多