我正在使用 Bootstrap 开发 Angular 应用。
为了尽量减少 Bootstrap 在我的 HTML 上的占用空间,我引入了两个表单指令:
form-control.js
module.directive('formControl', function() {
return {
restrict : 'E',
templateUrl : 'form-control.tmpl.html',
scope: {
label: '@'
},
transclude : true
};
});
form-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>
我还对该指令进行了一些“扩展”,用于各种表单输入字段。例如:
表单输入文本.js
module.directive('formInputText', function() {
return {
restrict : 'E',
templateUrl : 'form-input-text.tmpl.html',
scope: {
label: '@',
value: '=ngModel'
}
};
});
form-input-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>
直接绑定(bind)到 transcludeScope
的属性. transcludeScope.value
直接改了,父作用域isolateScope
不受影响。因此,输入中的任何模型更改都不会返回到 appScope
。 .
我想做的是在 appScope.person.name
之间创建双向绑定(bind)和 isolateScope
的嵌套属性,例如isolateScope.model.value
.
理想情况下,我想这样声明我的指令:
表单输入文本.js
module.directive('formInputText', function() {
return {
restrict : 'E',
templateUrl : 'form-input-text.tmpl.html',
scope: {
model: {
label: '@',
value: '=ngModel'
}
}
};
});
这将允许嵌入的部分绑定(bind)到 model.value
,这将使更改对 isolateScope 可见,而 isolateScope 又会在 isolateScope
中传播更改返回appScope
.
Angular 似乎不直接支持这种用法。
谁能指出支持此用例的 Angular 功能,或者如果不支持,请提供解决方法?
编辑:
目前,我的解决方案是内联 form-control
模板进入 form-input-text
模板。
form-input-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
,但它也复制了标记,这是我希望重构到一个地方的。
最佳答案
考虑作用域实际上有点走错了路,我认为嵌入与它没有太大关系。要“正确地”执行此操作,您应该与 ngModelController
集成。这允许任何以后集成的解析器和格式化程序(可能包含验证逻辑)在适当的时间运行。这有点复杂,因为你有 2 个:应用程序中的父级和指令模板中的父级,每个都有 2 个“管道”要集成:
- 模型值(value) -> 观点值(value)
- View 值 -> 模型值
父 ngModelController 的 View 值然后用作内部 ngModelController 的模型值。所以整体管道看起来像
- 父模型值 -> 父 View 值 -> 内部模型值 -> 内部 View 值
- 内部 View 值 -> 内部模型值 -> 父 View 值 -> 父模型值
为此:
确保您在指令定义中
require: 'ngModel'
,以访问父级ngModelController
从父级 ngModelController 到内部的更改是使用父级
ngModelController
的$render
方法完成的,使用其$viewValue
。这可确保父级$formatters
中的任何函数都已运行。用户从内部指令发起的更改是通过向其
$viewChangeListeners
数组添加一个函数来完成的,该函数调用父ngModelController 上的
。要从链接函数范围访问它,您需要一个命名的表单和输入元素。一个小麻烦是,表单仅在指令的链接功能运行后才在指令范围内注册,因此您需要观察者才能访问它。$setViewValue
以防万一,请确保
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);
});
});
}
};
});
它的模板看起来像
<form-control label="{{label}}" ng-form name="form">
<input type="text"
class="form-control"
name="input"
ng-model="model.value">
</form-control>
这可以在 http://plnkr.co/edit/vLGa6c55Ll4wV46a9HRi?p=preview 上看到
关于javascript - 如何协调 Angular 的 "always use dots with ngModel"规则与隔离范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26459958/