我们有一个在许多应用程序中使用的联系表单。有很多重复的默认值、验证规则、结构等。我们正在研究一组指令,以使 View 更语义化、更简洁。
我们正在瞄准几个目标。
在父指令中定义一次联系表单模型,如下所示:
<div my-form model='formModel'>
。关联的子指令将能够从model
获取基本模型属性。为每个输入提供默认配置(大小、验证规则、占位符、类等),但允许在必要时覆盖属性。因此,我们使用
my-form
创建子指令指令的通信 Controller 。我们还希望这些子指令绑定(bind)到应用程序 Controller 的模型formModel
。
我在实现这个时遇到了一些麻烦。
formModel
通过父指令的 Controller 公开,但我必须手动$compile
子指令使用scope.$parent
在link
功能。这对我来说似乎很臭,但如果我尝试使用子指令的作用域,编译后的 HTML 包含正确的属性(它在源代码中可见),但它没有绑定(bind)到 Controller ,并且在以下情况下它不会出现在任何作用域中:用Batarang检查。我猜我添加属性太晚了,但不确定如何更早添加属性。虽然我可以只使用
ng-model
对于每个子指令,这正是我想要避免的。我希望生成的 View 非常干净,并且必须在每个字段上指定模型名称是重复的且容易出错。我还能如何解决这个问题?
这是一个jsfiddle它对我想要完成的任务有一个有效但“臭”的设置。
angular.module('myApp', []).controller('myCtrl', function ($scope) {
$scope.formModel = {
name: 'foo',
email: '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="33555c5c73555c5c5152411d5d5647" rel="noreferrer noopener nofollow">[email protected]</a>'
};
})
.directive('myForm', function () {
return {
replace: true,
transclude: true,
scope: true,
template: '<div ng-form novalidate><div ng-transclude></div></div>',
controller: function ($scope, $element, $attrs) {
$scope.model = $attrs.myModel;
this.getModel = function () {
return $scope.model;
};
}
};
})
.directive('myFormName', function ($compile) {
return {
require: '^myForm',
replace: true,
link: function (scope, element, attrs, parentCtrl) {
var modelName = [parentCtrl.getModel(),attrs.id].join('.'),
template = '<input ng-model="' + modelName + '">';
element.replaceWith($compile(template)(scope.$parent));
}
};
});
最佳答案
有一个更简单的解决方案。
家长表格指令
首先,为父表单指令建立一个隔离范围,并使用 2 路绑定(bind)导入 my-model
属性。这可以通过指定 scope: { model:'=myModel'}
来完成。实际上没有必要指定原型(prototype)范围继承,因为您的指令没有使用它。
您的隔离作用域现在已导入“模型”绑定(bind),我们可以使用此事实来编译和链接子指令针对父作用域。为此,我们将从父指令中公开一个compile 函数,子指令可以调用该函数。
.directive('myForm', function ($compile) {
return {
replace: true,
transclude: true,
scope: { model:'=myModel'},
template: '<div ng-form novalidate><div ng-transclude></div></div>',
controller: function ($scope, $element, $attrs) {
this.compile = function (element) {
$compile(element)($scope);
};
}
};
子现场指令
现在是时候设置您的子指令了。在指令定义中,使用 require:'^myForm'
指定它必须始终位于父表单指令中。在编译函数中,添加 ng-model="model.{id attribute}"
。不需要弄清楚模型的名称,因为我们已经知道“模型”将在父范围中解析为什么。最后,在链接函数中,只需调用您之前设置的父 Controller 的编译函数即可。
.directive('myFormName', function () {
return {
require: '^myForm',
scope: false,
compile: function (element, attrs) {
element.attr('ng-model', 'model.' + attrs.id);
return function(scope, element, attrs, parentCtrl) {
parentCtrl.compile(element);
};
}
};
});
这个解决方案是最小的,只需很少的 DOM 操作。它还保留了针对父范围编译和链接输入表单字段的初衷,并尽可能减少干扰。
关于AngularJS : Child input directive needs to compile in the scope of its parent for ng-model to bind,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21943242/