Angular嵌套指令不在模型中显示新实体

时间:2015-01-14 03:44:29

标签: angularjs angularjs-directive

开发一个角度应用程序,其中包含一个用于构建目录/嵌套树结构的功能...

我遇到的问题是节点的渲染并没有按预期工作。

当列表中已有产品节点并且可以创建部分但是尝试将子部分添加到已添加的部分时,似乎只渲染产品。部分和产品节点按预期插入到模型中 - 只是指令似乎不在原始模型中不存在的节点上运行。

相关代码:

HTML          

<head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.7/angular.js" data-semver="1.3.7"></script>
    <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
    <h1>Menu</h1>
    <button ng-click="addSection()">Add</button>
    <admin-sections sections="menu.sections"></admin-sections>
</body>

</html>

JS

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

app.controller('MainCtrl', function($scope) {
  $scope.menu = {
    sections: [{
      name: "NEW SECTION 1",
      sections: [{
        name: "NEW SECTION",
        sections: [],
        products: [{
          "name": "Product",
          "price": "0.00"
        }]
      }],
      products: []
    }]
  };

  $scope.addSection = function() {
    $scope.menu.sections.push({
      name: "NEW SECTION",
      sections: [],
      products: []
    });
  };
});

app
  .directive('adminSections', function() {
    return {
      restrict: "E",
      replace: true,
      scope: {
        sections: '='
      },
      templateUrl: 'sections.html'
    };
  })
  .directive('adminSection', function($compile) {
    return {
      restrict: "E",
      replace: true,
      scope: {
        section: '='
      },
      templateUrl: 'section.html',

      link: function(scope, element, attrs, controller) {
        if (angular.isArray(scope.section.sections) && scope.section.sections.length > 0) {
          element.append($compile('<admin-sections sections="section.sections"></admin-sections>')(scope));
        }
        if (angular.isArray(scope.section.products) && scope.section.products.length > 0) {
          element.append($compile('<admin-products products="section.products"></admin-products>')(scope));
        }

        scope.addSub = function(section) {
          section.sections.push({
            "name": "NEW SECTION",
            "sections": [],
            "products": []
          });
        };

        scope.addProduct = function(section) {
          section.products.push({
            "name": "Product",
            "price": "0.00"
          });
        };

        scope.deleteSection = function(section) {
          var idx = scope.$parent.sections.indexOf(section);
          scope.$parent.sections.splice(idx, 1);
        };
      }
    };
  })
  .directive('adminProducts', function() {
    return {
      restrict: "E",
      replace: true,
      scope: {
        products: '='
      },
      templateUrl: 'products.html',
      link: function(scope, element, attrs, controller) {
        scope.editProduct = function(product) {
          if (product.price === undefined) {
            product.price = 0;
          }
          element.append($compile('<productform product="product"></productform>')(scope));
        };

        scope.deleteProduct = function(idx) {
          if (confirm('Are you sure you want to delete this product?\n\nClick OK to confirm.')) {
            scope.products.splice(idx, 1);
          }
        };
      }
    };
  })
  .directive('adminProduct', function($compile) {
    return {
      restrict: "E",
      replace: true,
      scope: {
        product: '='
      },
      templateUrl: 'product.html',
      link: function(scope, element, attr, controller) {

        scope.editProduct = function(product) {
          if (product.price === undefined) {
            product.price = 0;
          }
          element.append($compile('<productform product="product" />')(scope));
        };

        scope.deleteProduct = function(idx) {
          scope.$parent.deleteProduct(idx);
        };
      }
    };
  })
  .directive('productform', function($compile) {
    return {
      restrict: "E",
      replace: true,
      scope: {
        product: "="
      },
      templateUrl: 'productform.html',
      link: function(scope, element, attrs, controller) {
        scope.orig = angular.copy(scope.product);
        scope.ok = function() {
          element.remove();
          scope.$parent.editMode = false;
        };

        scope.cancel = function() {
          scope.reset();
          element.remove();
          scope.$parent.editMode = false;
        }

        scope.reset = function() {
          scope.product = angular.copy(scope.orig);
        }
      }
    };
  });

Plunker在这里:Angular Tree Menu

希望你能看到意图。

1 个答案:

答案 0 :(得分:3)

问题是你在链接指令时添加列表,这取决于调用链接函数时的部分状态(只有一次,当有角度看到它时)。

当你添加一个新的子节时,它是链接的,但它的子节列表是空的,所以它没有,并且结果元素没有子节,因为你添加admin-sections取决于当时的子节数组状态调用链接函数,因此根本不会添加嵌套指令。

简单地删除if语句就足够了(或只是检查它们是否是数组):

element.append($compile('<admin-sections sections="section.sections"></admin-sections>')(scope));

element.append($compile('<admin-products products="section.products"></admin-products>')(scope));

这样,指令中的ng-repeat将在每个部分中观察部分数组并相应地更新列表,而在数组为空时保持为空。

Working Plunker


关于嵌套指令的工作方式,这里有一个很好的article on嵌套指令&#39;链接和控制器函数被调用。

通常,在解析任何内部指令之前运行controller,并在之后运行link。所以如果你有这样的嵌套指令:

<outer-directive>
    <inner-directive></inner-directive>
</outer-directive>

订单如下:

  1. 外部指令控制器
  2. 内部指令控制器
  3. 内部指令链接
  4. 外部指令链接
  5. 这就是为什么当我尝试将admin-sections指令添加到每个部分的模板时,解析器进入无限循环。解析每个部分意味着调用该部分的另一个link子部分,但在外部$compile的链接函数中使用admin-section意味着它将在外部指令之后进行解析处理完毕。

    此外,内部指令可requiredocs)父指令使用其控制器。