我正在使用Bootstrap开发一个Angular应用程序。
为了最大限度地减少HTML上的Bootstrap占用空间,我已经为表单引入了两个指令:
形状control.js
module.directive('formControl', function() {
return {
restrict : 'E',
templateUrl : 'form-control.tmpl.html',
scope: {
label: '@'
},
transclude : true
};
});
形状control.tmpl.html
<div class="form-group">
<label class="control-label col-sm-2">
{{ label }}
</label>
<div class="col-sm-10"
ng-transclude>
</div>
</div>
对于各种表单输入字段,我还对该指令有一些“扩展”。 e.g:
形式输入-text.js
module.directive('formInputText', function() {
return {
restrict : 'E',
templateUrl : 'form-input-text.tmpl.html',
scope: {
label: '@',
value: '=ngModel'
}
};
});
形式输入-text.tmpl.html
<form-control label="{{label}}">
<input type="text"
class="form-control"
ng-model="value">
</form-control>
app.html
<form-input-text label="Name"
ng-model="person.name">
</form-input-text>
这里我遇到了一个问题。在这个例子中有许多范围:
appScope = { person : { name : "John" } };
isolateScope = {
label: "Name",
value: "John" // bound two-way with appScope.person.name
};
transcludeScope = {
__proto__: isolateScope,
label: "Name", // inherited from isolateScope
value: "John" // inherited from isolateScope
};
如果我更改输入文本框中的文本,则仅修改transcludeScope
:
appScope = { person : { name : "John" } };
isolateScope = {
label: "Name",
value: "John" // bound two-way with appScope.person.name
};
transcludeScope = {
__proto__: isolateScope,
label: "Name", // inherited from isolateScope
value: "Alice" // overrides value from isolateScope
};
这是因为<input>
直接绑定到transcludeScope
的属性。 transcludeScope.value
直接更改,父作用域isolateScope
不受影响。因此,输入中的任何模型更改都不会使其返回appScope
。
我想要做的是在appScope.person.name
和isolateScope
的嵌套属性之间创建双向绑定,例如isolateScope.model.value
。
理想情况下,我想宣布我的指令:
形式输入-text.js
module.directive('formInputText', function() {
return {
restrict : 'E',
templateUrl : 'form-input-text.tmpl.html',
scope: {
model: {
label: '@',
value: '=ngModel'
}
}
};
});
这将允许被转换的部分绑定到model.value
,这会使isolateScope更改可见,这会将isolateScope
中的更改传播回appScope
。
Angular似乎没有直接支持此用法。
任何人都可以向我指出支持此用例的Angular功能,或者如果没有,提供解决方法吗?
编辑:
目前,我的解决方案是将form-control
模板内嵌到form-input-text
模板中。
形式输入-text.tmpl.html
<div class="form-group">
<label class="control-label col-sm-2">
{{ label }}
</label>
<div class="col-sm-10">
<input type="text"
class="form-control"
ng-model="value">
</div>
</div>
这消除了ng-transclude
引入的子范围,但它也复制了标记,这是我希望重构到一个地方的。
答案 0 :(得分:2)
对范围的思考实际上是在错误的轨道上进行,我不认为翻译与它有很大关系。要“正确”执行此操作,您应该与ngModelController
集成。这允许任何后来的集成解析器和格式化程序(可能包含验证逻辑)在适当的时间运行。它有点复杂,因为你有2个:应用程序中的父元素和指令模板中的元素,每个都有2个“管道”要集成:
然后将父ngModelController的视图值用作内部ngModelController的模型值。因此总体管道看起来像
要做到这一点:
确保您在指令定义中require: 'ngModel'
有权访问父ngModelController
从父级ngModelController到内部的更改是使用父$render
的{{1}}方法完成的,使用其ngModelController
。这可以确保父$viewValue
中的任何函数都已运行。
内部指令的用户启动更改是通过向其$formatters
数组添加一个函数来完成的,该数组在父$viewChangeListeners
上调用$setViewValue
。要从链接函数作用域访问它,您需要一个命名表单和输入元素。一个轻微的烦恼是表单只在指令的链接功能运行后才在指令的范围内注册,所以你需要一个观察者来访问它。
如果出现任何奇怪现象,请确保ngModelController
中的模型位于对象中。 (我不确定这在技术上是否必要)
然后不需要将模型放在内部指令的formInputText
对象中。
把它们放在一起,
scope
它的模板看起来像
app.directive('formInputText', function() {
return {
restrict : 'E',
templateUrl : 'form-input-text.tmpl.html',
scope: {
label: '@'
},
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
scope.model = {};
// Propagate changes from parent model to local
ngModelController.$render = function() {
scope.model.value = ngModelController.$viewValue;
};
// Propagate local user-initiated changes to parent model
scope.$watch('form', function(form) {
if (!form) return;
form.input.$viewChangeListeners.push(function() {
ngModelController.$setViewValue(form.input.$modelValue);
});
});
}
};
});
答案 1 :(得分:1)
我将为您的案例使用自定义控件,如here所述,使自定义<form-input-*>
指令成为真正的控件。它确实需要一些额外的工作。概述解决方案的简单版本:
<强>形式输入-text.js 强>
app.directive('formInputText', function() {
return {
restrict : 'E',
template : '<form-control label="{{label}}"><input type="text" class="form-control" /></form-control>',
scope: {
label: '@'
},
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
var input = angular.element(elem[0].querySelectorAll("input")[0]);
ngModel.$render = function() {
input.val(ngModel.$viewValue || '');
};
input.on('blur keyup change', function() {
scope.$apply(read);
});
function read() {
ngModel.$setViewValue(input.val());
}
}
};
});
简而言之,根据文档,您require
ngModel
并实施其方法。 ngModel
只是应用于您的控件的另一个指令,其他功能也可以使用,例如自定义验证程序,ng-required
等。
工作小提琴:http://jsfiddle.net/1n53q59z/
请记住,根据您的使用情况,您可能需要进行一些调整。