Angular - 使ng-bind能够处理嵌套指令

时间:2017-03-31 15:21:15

标签: angularjs

我已经构建了一个小模式,可以根据模型递归创建不同的子指令。我正在使用$compile递归地构建子指令,然后将它们附加到父节点 指令构建本身似乎工作正常,但由于某种原因,嵌入式表达式或ng-bind或插值似乎不适用于嵌套指令。

这是一个片段:

app.directive("child", function ($compile) {
  function getTemplate(depth) {
    if (depth % 2 == 0) {
      return "<even depth='deeper'/>"
    } else {
      return "<odd depth='deeper'/>"
    }
  }
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element) {
      if ($scope.depth == 0) {
        var child = angular.element("<span ng-bind='depth'/>");
        child = $compile(child)($scope);
        $element.append(child);
      } else {
        $scope.deeper = $scope.depth - 1;
        var child = angular.element(getTemplate($scope.depth));
        child = $compile(child)($scope);
        $element.append(child);
      }
    }
  }
})

在此测试中,指令将以递归方式向下潜行,直至depth到达0,然后吐出<span>元素。
预期结果应为span元素,其值为0。但它似乎没有评估。使用<span>{{depth}}</span>也会导致文字html而不是评估内容 我正在尝试实现嵌套<even><odd><even>指令的结果 删除 周围的<child> - 指令。

这是一个完整的jsFiddle:https://jsfiddle.net/eg1e1aLz/

生成的DOM应如下所示:

<test depth="4" class="ng-isolate-scope">
  <even depth="depth-1" class="ng-scope ng-isolate-scope">
    <odd depth="depth-1" class="ng-scope ng-isolate-scope">
      <even depth="depth-1" class="ng-scope ng-isolate-scope">
        <odd depth="depth-1" class="ng-scope ng-isolate-scope"><span ng-bind="depth" class="ng-binding ng-scope">0</span></odd>
      </even>
    </odd>
  </even>
</test>

6 个答案:

答案 0 :(得分:3)

根据您在下面的评论和问题的更新,以下内容......

它使用模板函数getTemplate来构建结构,并使用相同的指令:oddeven作为构建功能的占位符。

var app = angular.module("app", []);

app.directive("test", function($compile) {
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element, $attrs) {

      // template accessible to the child directives
      $scope.getTemplate = function(depth) {
        if (depth <= 0) {
          return "<span ng-bind='depth'/>"; // also bindings like {{depth}} work
        } else if (depth % 2 === 0) {
          return "<even depth='depth-1'></even>"; // bindings like {{depth}} work
        } else {
          return "<odd depth='depth-1'></odd>";
        }
      }

      var child = angular.element($scope.getTemplate($scope.depth));
      $compile(child)($scope);
      $element.append(child);
    }
  }
});
app.directive("odd", function($compile) {
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element) {
      $scope.getTemplate = $scope.$parent.getTemplate; // bring template into current scope
      var child = angular.element($scope.getTemplate($scope.depth));
      $compile(child)($scope);
      $element.append(child);
    }
  }
})
app.directive("even", function($compile) {
  return {
    scope: {
      depth: "=",
    },
    link: function linker($scope, $element) {
      $scope.getTemplate = $scope.$parent.getTemplate; // bring template into current scope
      var child = angular.element($scope.getTemplate($scope.depth));
      $compile(child)($scope);
      $element.append(child);
    }
  }
})

var controller = app.controller("controller", function($scope) {});

更新小提琴:https://jsfiddle.net/bda411fj/15/

<强>结果:

<test depth="4" class="ng-isolate-scope">
  <even depth="depth-1" class="ng-binding ng-scope ng-isolate-scope">
    <odd depth="depth-1" class="ng-binding ng-scope ng-isolate-scope">
      <even depth="depth-1" class="ng-binding ng-scope ng-isolate-scope">
        <odd depth="depth-1" class="ng-binding ng-scope ng-isolate-scope">
          <span ng-bind="depth" class="ng-binding ng-scope">0</span>
        </odd>
      </even>
    </odd>
  </even>
</test>

答案 1 :(得分:1)

模板的已编译部分只需在child.html()上作为子项引用 将您的代码更改为从$element.append(child.html());$element.append(child);

的3个位置

这将开始打印您要查找的深度值。还有其他你在找什么?

答案 2 :(得分:0)

你的html可能没有被清理,因为它没有编译尝试使用ngSanatize可以解决你的问题 https://docs.angularjs.org/api/ngSanitize/service/ $ sanitize 它将删除所有潜在危险的令牌和会给你一个合适的HTML。

答案 3 :(得分:0)

由于您不想使用html(),请不要使用child.html()。

使用孩子本身。

查看此处的文档

检查下面的代码段。根据您问题中的示例代码段添加

var app = angular.module("app", []);
app.directive("test", function($compile) {
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element, $attrs) {
      var child = angular.element("<child depth='depth'/>");
      child = $compile(child)($scope);
      $element.append(child);
    }
  }
});
app.directive("child", function($compile) {
  function getTemplate(depth) {
      return depth % 2 == 0 ? "<even depth='depth-1'/>" : "<odd depth='depth-1'/>"
  }
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element) {
      if ($scope.depth == 0) {
        var child = angular.element("<span ng-bind='depth'/>");
        child = $compile(child)($scope);
        $element.append(child);
      } else {
        var child = angular.element(getTemplate($scope.depth));
        child = $compile(child)($scope);
        $element.append(child);
      }
    }
  }
})
app.directive("odd", function($compile) {
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element) {
      var child = angular.element("<child depth='depth'/>");
      child = $compile(child)($scope);
      $element.append(child);
    }
  }
})
app.directive("even", function($compile) {
  return {
    scope: {
      depth: "="
    },
    link: function linker($scope, $element) {
      var child = angular.element("<child depth='depth'/>");
      child = $compile(child)($scope);
      $element.append(child);
    }
  }
})
var controller = app.controller("controller", function($scope) {});
body {
  padding: 5px 5px 5px 5px !important;
  font-size: 30px;
}

test,
child,
even,
odd {
  padding: 5px 5px 5px 5px;
  margin: 5px 5px 5px 5px;
  border: 1px solid black;
}

test {
  background-color: aliceblue !important;
}

child {
  background-color: beige !important;
}

even {
  background-color: lightgreen !important;
}

odd {
  background-color: lightcoral !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
<body ng-app="app" ng-controller="controller">
  <test depth="4"></test>
</body>

答案 4 :(得分:0)

你的jsfiddle非常好,唯一的问题是一点逻辑。

当你编译子指令时,你不应该仅仅附加child.html(),因为这会附加一个没有绑定的HTML和连接到它们的观察者。相反,你应该追加整个孩子

https://jsfiddle.net/eg1e1aLz/2/

child = $compile(child)($scope);
$element.append(child);

答案 5 :(得分:0)

很抱歉,如果我错过了你想要完成的内容,但我不相信你需要使用子指令或类似的东西。您可以使用test指令并不断构建子节点,然后将范围绑定到最后一个:

angular.module("app", [])
    .directive("test", function ($compile) {
        return {
            scope: {
                depth: "="
            },
            link: function linker($scope, $element, $attrs) {
                var element = $element;
                for (var x = 0; x < $scope.depth; x++) {
                    var elementType = x % 2 === 0 ? 'even' : 'odd';
                    var subElement = angular.element(document.createElement(elementType));
                    element.append(subElement);
                    element = subElement;
                }
                var span = angular.element('<span ng-bind="depth" />');
                element.append(span);
                $compile(span)($scope);
            }
        }
    })

小提琴:https://jsfiddle.net/ffyv0zmy/

我在你的例子中意识到你想要输出为“0”的深度,但你可以硬编码,因为我不确定它的用途。

结果HTML:

<test depth="4" class="ng-isolate-scope">
    <even>
        <odd>
            <even>
                <odd>
                    <span ng-bind="depth" class="ng-binding ng-scope">4</span>
                </odd>
            </even>
        </odd>
    </even>
</test>

关于这一点的好处是你不必将范围附加到偶数/奇数元素,除非你也想要。

你仍然可以将指令连接到每个奇数/偶数元素,但是你必须编译$元素而不是附加所有范围。