【问题标题】:Compiling dynamic content - AngularJS编译动态内容 - AngularJS
【发布时间】:2014-08-10 13:49:03
【问题描述】:

我正在重新措辞这个问题,因为我认为原文不太清楚。

基本上,我有一个“包装器”指令,我试图在其中动态地将属性添加到包装(转入)元素之一。我可以让它工作,但 Angular 似乎不知道一旦添加了新属性。

如果我使用$compile,那么 Angular 会识别它们——但代价是双重编译嵌入的内容,在这种情况下,它会将 select 标记中的 options 的数量加倍。

Here is a plunker 显示(使用 cmets)我正在尝试的内容,对于那些可以查看代码并仅通过查看提出答案的人来说,下面是相同的代码: (注意 - 我的最终目标是检查自定义指令 valid-form-grouprequired 属性,如果发现将其应用于包含的 select 标记)

HTML

<body ng-controller="MainCtrl">

  <form name="validationForm" novalidate>

    <valid-form-group class="form-group" ng-class="{'has-error': validationForm.validInfo.$error.required}" required>

      <select ng-model="data.option" ng-options="option.id as option.message for option in selectOptions" name="validInfo" id="validInfo">
        <option value="">-- Select a Question --</option>
      </select>

    </valid-form-group>

  </form>

</body>

JS

var app = angular.module('plunker', [])
  .controller('MainCtrl', function($scope) {
    $scope.selectOptions = [
      {id: 1, message: 'First option'}, 
      {id: 2, message: 'Second option'}, 
      {id: 3, message: 'Third option'}
    ];
  })
  .directive('validFormGroup', function($compile) {
    return {
      restrict: 'E',
      template: '<div><span ng-transclude></span></div>',
      replace: true,
      transclude: true,
      require: '^form',
      link: function(scope, element, attrs, ctrl) {

        if (attrs.required !== undefined) {

          var selectElement = angular.element(element.find('select'));
          // either of the below produce the same results
          selectElement.attr('ng-required', true);
          //selectElement.attr('required', true);

          // if the below is commented out it wont validate
          // BUT if it is left in it will recompile and add another 3 options
          $compile(selectElement)(scope); 
        }
      }
    };
  });

CSS

.has-error{
  border: solid 1px red;
}

请注意,这里的示例使用 'required'(或 ng-required)作为添加的属性,以强调 Angular 除非编译,否则无法识别它。

欢迎任何帮助或 cmets - 我无法让这个工作有点失望,所以也许我缺少一些基本的东西......

plunker 应该有助于可视化我的问题。

edit - 对延迟回复答案和 cmets 表示歉意。正如下面一两条评论中提到的,个人问题使我无法抽出时间进行调查。

【问题讨论】:

  • 你不能在项目上加上条件ng-required:`ng-required="requiredItem"。喜欢建议here
  • @Ben - 我在上面的代码示例中有一条评论://以下任何一个都会产生相同的结果 selectElement.attr('ng-required', true); //selectElement.attr('required', true);不幸的是(虽然这仍然动态添加属性)它仍然没有被 Angular 渲染。注意 - 我在这里使用“必需”作为示例。这可以指任何可能包含需要被 Angular “识别”的内容的指令......
  • 您为什么要尝试将这个内部指令包装起来?可以通过这样的简单方式完成plnkr.co/edit/oFyDTa2anUQCLWOkwrW5?p=preview
  • 我正在为表单组内容编写一个包装器,其中涉及的内容远不止上述内容(即根据标签信息等动态添加带有相关错误消息的跨度)。但是,我坚持上述概念——而且我并不特别依赖于“包含”或“ng-include”——这只是为了说明我的问题。不过,感谢您的关注-如果您考虑到这一点重新审视这个问题,将不胜感激。它快把我逼疯了! :)

标签: javascript html angularjs


【解决方案1】:

试试这个简单的指令:

.directive('validFormGroup', function($compile) {
    return {
        restrict: 'A',
        replace: false,
        require: '^form',
        compile: function (element, attr) {
            if (attr.required !== undefined) {
                var selectElement = element.find('select');
                // either of the below produce the same results
                selectElement.attr('ng-required', true);
                //selectElement.attr('required', true);
            }
        }
    };
});

并将其用作 html 属性:

<div valid-form-group class="form-group" ng-class="{'has-error': validationForm.validInfo.$error.required}" required>

      <select ng-model="data.option" 
      ng-options="option.id as option.message for option in selectOptions"
      name="validInfo" id="validInfo" >
<option value="">-- Select a Question --</option>
</select>
      <br/>
      <br/>Required invalid? {{validationForm.validInfo.$error.required||false}}
      <br/>
      <br/>

</div>

DEMO

解释

  • 我在这个解决方案中根本不使用transclude,因为这个指令的目的只是在使用范围编译之前修改html,不需要transcluded内容过于复杂。

  • 这里我处理 compile 函数而不是 link 函数。 compile 函数是您修改 html 在链接到范围之前的好地方。

【讨论】:

  • 我同意,对于这个简单的示例,您无需使用 transclude 即可摆脱困境。然而,OP 在 cmets 中表示他正在研究比示例发布的更复杂的东西。
  • @Beyers:复杂,OP 希望能够modify html dynamically。如果我们需要做更复杂的事情,只需在 compile 函数中编写更多代码即可。
  • 这似乎要简单得多 - Beyers 是正确的,因为我的解决方案比这个示例更复杂,我会看看 Khanh 在这里建议的内容是否适用于整个场景。我假设compile 函数发生在之后 已执行嵌入,但之前 链接到范围已经发生(根据您上面的解释)请注意我在电脑目前有限(生病的孩子),但会尽快全力以赴。
  • 另一个快速评论 - 我在链接函数中使用controller 来获取其他动态代码的表单名称。使用上面的 ^form 选项,是否可以在 compilefunction 中获取要使用的表单名称?
  • @Sean:我不知道是否可以在compile 中获取表单控制器。但我认为在link 函数中获取form 更有意义。 compile 是关于修改template ,这意味着它是not functional yetlink 发生在您的模板为 linked(功能性)时。当指令还没有起作用时,访问表单控制器是没有意义的,因为控制器是关于处理功能逻辑的。
【解决方案2】:

我只能猜测您看到的是指令初始化过程中双重编译的结果,这就是您看到双重选项集的原因。

您可以通过将编译包在 $timeout 中来解决这个问题,这将确保编译发生在指令初始化之外。这是一个有效的demo 和下面的指令代码:

.directive('validFormGroup', function($compile, $timeout) {
  return {
    restrict: 'E',
    template: '<div><span ng-transclude></span></div>',
    replace: true,
    transclude: true,
    require: '^form',
    link: function(scope, element, attrs, ctrl) {
      if (attrs.required !== undefined) {
        var selectElement = angular.element(element.find('select'));
        $timeout(function(){
          selectElement.attr('ng-required', true);
          $compile(selectElement)(scope);
        });
      }
    }
  };
});      

PS 您可以通过在指令上使用隔离范围然后检查您的嵌入输入/选择元素是否具有所需的属性集来实现类似的引导包装器功能。在隔离作用域上定义一个函数来检查错误并将这个函数绑定到 form-group ng-class has-error。这样你就不必使用 $timeout 了。

【讨论】:

  • 谢谢 Beyers - 我得试一试。是否可以解释为什么在指令初始化之外进行编译会产生不同的效果?家里有几个生病的孩子,所以我不确定我什么时候可以检查出来 - 但一旦测试就会接受你的答案。
【解决方案3】:

我想在这里建议一种不同的方法,我将其用于动态验证。向字段动态添加验证,减少每个字段的样板 html 代码,我已经为类似目的编写了一个指令。请查看 plunker 链接,例如指令...PLUNKER

我已经为所有类型的字段编写了这样的指令:数字、文本、选择、文本区域、布尔值、日期选择器等...所附的 plunker 为您提供了一个文本和数字字段的示例。

角度的魔力发生在下面的衬里:

  var newElem = angular.element(template);
  element.replaceWith(newElem);
  $compile(newElem)(scope);

所有其他代码只是一些逻辑 if else 部分..

【讨论】:

  • 您好算法,我喜欢您管理动态验证的方式,这与我自己想要实现的目标相似。不过,我发布了我的确切问题的基本示例 - 那是当您涉及 select 元素时。您是否尝试过对绑定到某个 scope 集合的 select 元素进行动态验证?根据上述,我发现 compile 语句(您也使用)将集合中的项目加倍。
  • 是的,我也对 select 进行了动态验证,尽管只有 ng-required 因为其他不适用。我也在做的是创建一个新元素并替换它,所以它不会导致重复属性问题
【解决方案4】:

在这种情况下你不需要使用 $compile。我只是更改了代码来解决您的问题。我尽量与您的原始版本保持一致,以帮助您理解。

Javascript(根据 attrs.required 添加 isRequired 范围变量)

  .directive('validFormGroup', function($compile) {
    return {
      restrict: 'E',
      template: '<div><span ng-transclude></span></div>',
      replace: true,
      transclude: true,
      require: '^form',
      link: function(scope, element, attrs, ctrl) {
        if (attrs.required !== undefined) {
          //added isRequired
          scope.isRequired = true;

          var selectElement = angular.element(element.find('select'));
          // either of the below produce the same results
          //selectElement.attr('ng-required', true);
          //selectElement.attr('required', true);

          // if the below is commented out it wont validate
          // BUT if it is left in it will recompile and add another 3 options
          //remove $compile
          //$compile(selectElement)(scope); 
        }
      }
    };
  });

HTML(添加 ng-required=isRequired)

<select ng-model="data.option" ng-options="option.id as option.message for option in selectOptions" name="validInfo" id="validInfo" ng-required="isRequired">

您可以在http://plnkr.co/edit/BGQo05mTNr1H1HjFXSpf?p=preview参考plunkr版本

我认为这回答了你的问题。如果你有更复杂的场景,请分享。

更新:(阅读其他评论并再次回答后) 如果您需要动态 HTML 内容,可以使用此解决方案 - Compiling dynamic HTML strings from database

另一种方法是删除 ng-*attribute 以防止重新编译。 plunkr 版本http://plnkr.co/edit/JpfdvISCZ39heuUfdHt3?p=preview

      selectElement.removeAttr('ng-options');
      selectElement.removeAttr('ng-model');

【讨论】:

  • 嗨 hutingung - 我想你误解了我想要达到的目标。我想要的是使用出现在valid-form-group 指令中的属性,以应用于在要被嵌入的html 中找到的input 元素。换句话说,如果在valid-form-group 指令上找到required,则应将其动态添加到示例内容中的select 控件中。我希望能更清楚地解释它。
【解决方案5】:

我没有完整的答案,但是如果你给你的指令一个独立的范围范围:{},那么在双重编译时它无法获得选项,所以默默地失败 - 我在你的 plunkr 中试过这个,我下拉菜单中只有一组选项。

我说我没有一个完整的答案 - 我不相信它 - 这似乎是一个 hack,我认为您确实需要与您可能能够分享的指令共享范围内的一些内容通过继承范围单独继承这些,以便您可以维护功能,但正如我所说,它感觉很hacky并且感觉不对,因为它没有解决如何最好地处理双重编译的基本问题。

【讨论】:

    猜你喜欢
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 2019-08-08
    • 2016-06-29
    相关资源
    最近更新 更多