Angular Nested Directive Does Not Display New Objects in Model

Development of an angular application that includes a function for creating a directory / nested tree structure ...

The problem I am facing is rendering nodes that are not working properly.

It seems that products are only displayed if there is already a node product in the list and sections can be created, but an attempt to add a subsection to the one that was added is not displayed. The segment and product nodes are inserted into the model, as expected, simply so that the directives do not work on nodes that were not present in the original model.

Relevant Code:

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 is here: Angular Tree Menu

I hope you see the intention.

+7
angularjs angularjs-directive
source share
1 answer

The problem is that you add a list when the directive is bound, depending on the state of the section when you call the bind function (only once when angular sees this).

When you add a new subsection, it is linked, but its list of subsections is empty, so it does not have a single element, and the resulting element does not have subsections, since you add admin-sections depending on the state of the subsection array at the time the binding function therefore, no nested directives will be added at all.

Just deleting if should be enough (or just checking to see if they are arrays):

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

Thus, ng-repeat in your directives will look at the arrays of sections in each section and accordingly update the list, remaining empty when the array is empty.

Working plunker


Regarding how the nested directives work, here is a good article when the bind and nested directive functions are called.

In the general case, the controller starts before the analysis of all internal directives, and the link starts after. Therefore, if you have nested directives, such as:

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

The order will be as follows:

  • external directory controller
  • internal directive controller
  • internal directory link
  • external directive link

This is why when I tried to add the admin-sections directive to each section template, the parser went into an endless loop. Parsing each section meant calling another link in the subsections of this section, but using $compile in the external binding function, admin-section means that it will be parsed after processing the external directive.

In addition, internal directives can use require ( docs ) parent directives to use their controllers.

+3
source share

All Articles