Angular 嵌套指令未在模型中显示新实体
Angular nested directive not displaying new entities in model
正在开发一个 angular 应用程序,其中包含构建 directory/nested 树结构的功能...
我遇到的问题是节点的渲染没有按预期工作。
产品似乎仅在列表中已有产品节点时才会呈现,并且可以创建部分,但尝试向已添加的部分添加子部分不会呈现。部分和产品节点正按预期插入到模型中 - 只是指令似乎在原始模型中不存在的节点上不起作用。
相关代码:
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
希望你能看清意图。
问题是您在 linked 指令时添加列表,这取决于调用 linking 函数时该部分的状态(只有一次,当 angular看到它)。
当你添加一个新的小节时,它是 linked 但它的小节列表是空的所以它有 none,并且结果元素没有小节,因为你添加 admin-sections
取决于调用 linking 函数时的子部分数组状态,因此根本不会添加嵌套指令。
只需删除 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
将监视每个部分中的部分数组并相应地更新列表,同时在数组为空时保持为空。
关于嵌套指令的工作方式,这里有一个很好的 article 关于何时调用嵌套指令的 linking 和控制器函数。
一般来说,controller
s 是 运行 在解析任何内部指令之前,link
s 在 运行 之后。所以如果你有这样的嵌套指令:
<outer-directive>
<inner-directive></inner-directive>
</outer-directive>
顺序是这样的:
- 外部指令控制器
- 内部指令控制器
- 内部指令link
- 外部指令link
这就是为什么当我尝试将 admin-sections
指令添加到每个部分的模板时,解析器进入无限循环的原因。解析每个部分意味着调用该部分的另一个 link
,但在外部 admin-section
的 linking 函数中使用 $compile
意味着它将在处理外部指令后被解析.
此外,内部指令可以 require
(docs) 个父指令来使用它们的控制器。
正在开发一个 angular 应用程序,其中包含构建 directory/nested 树结构的功能...
我遇到的问题是节点的渲染没有按预期工作。
产品似乎仅在列表中已有产品节点时才会呈现,并且可以创建部分,但尝试向已添加的部分添加子部分不会呈现。部分和产品节点正按预期插入到模型中 - 只是指令似乎在原始模型中不存在的节点上不起作用。
相关代码:
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
希望你能看清意图。
问题是您在 linked 指令时添加列表,这取决于调用 linking 函数时该部分的状态(只有一次,当 angular看到它)。
当你添加一个新的小节时,它是 linked 但它的小节列表是空的所以它有 none,并且结果元素没有小节,因为你添加 admin-sections
取决于调用 linking 函数时的子部分数组状态,因此根本不会添加嵌套指令。
只需删除 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
将监视每个部分中的部分数组并相应地更新列表,同时在数组为空时保持为空。
关于嵌套指令的工作方式,这里有一个很好的 article 关于何时调用嵌套指令的 linking 和控制器函数。
一般来说,controller
s 是 运行 在解析任何内部指令之前,link
s 在 运行 之后。所以如果你有这样的嵌套指令:
<outer-directive>
<inner-directive></inner-directive>
</outer-directive>
顺序是这样的:
- 外部指令控制器
- 内部指令控制器
- 内部指令link
- 外部指令link
这就是为什么当我尝试将 admin-sections
指令添加到每个部分的模板时,解析器进入无限循环的原因。解析每个部分意味着调用该部分的另一个 link
,但在外部 admin-section
的 linking 函数中使用 $compile
意味着它将在处理外部指令后被解析.
此外,内部指令可以 require
(docs) 个父指令来使用它们的控制器。