javascript-如何调和Angular的“始终将点与ngModel一起使用”规则与隔离范围?
作者:互联网
我正在使用Bootstrap开发Angular应用程序.
为了最大程度地减少HTML上的Bootstrap占用空间,我为表单引入了两个指令:
表格控件
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>直接绑定到transcludeScope的属性.直接更改transcludeScope.value,并且父范围isolateScope不受影响.因此,输入中的任何模型更改都不会使其返回到appScope.
我想做的是在appScope.person.name和isolateScope的嵌套属性之间创建双向绑定,例如孤立范围.模型.值.
理想情况下,我想这样声明我的指令:
表单输入文本.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-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条“管道”要与之集成:
>模型值->查看价值
>查看价值->模型值
然后,将父ngModelController的视图值用作内部ngModelController的模型值.所以整体管道看起来像
>父模型值->父视图值->内部模型值->内在价值
>内部视图值->内部模型值->父视图值->父模型值
去做这个:
>确保您需要:指令定义中的“ ngModel”才能访问父级ngModelController
>从父ngModelController到内部的更改是使用父ngModelController的$render方法及其$viewValue完成的.这样可以确保父$formatters中的所有功能都已运行.
>用户通过internal指令启动的更改是通过在$viewChangeListeners数组中添加一个函数来完成的,该数组在父ngModelController上调用$setViewValue.要从链接函数范围访问此函数,您需要一个命名表单和输入元素.有点烦人的是,该表单仅在其链接功能运行后才在该指令的范围内注册,因此您需要一个观察者来访问它.
>以防万一,请确保formInputText中的模型在对象中. (我不确定这在技术上是否必要)
>然后,您无需将模型放在内部指令的作用域对象中.
放在一起
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下工作
标签:angularjs,twitter-bootstrap,angularjs-scope,javascript 来源: https://codeday.me/bug/20191121/2049516.html