如何正确控制Angular指令的$dirty和$pure状态
How to correctly control the $dirty and $pristine state of an Angular directive
我正在尝试创建一个角度指令,该指令将组成一个KendoUI TreeView控件和一个文本输入,以便它搜索输入的文本并选择项目。我有这个功能,但我希望指令在表单中表现良好,这样当复选框值更改时,就会设置表单的脏属性。
不幸的是,现在的行为是,当有人在输入中输入文本时,from会被设置为dirty(这不是所需的行为),并且选中复选框没有效果。
我希望在表单上调用$setDirty(),但它对我的函数不可见,我希望有一种干净的方法可以使它可见,而不是将它添加到$scope中,这也不能解决$dirty设置为true但输入搜索文本的问题。
我创建了一个Plunk来演示这个问题。
这是指令的代码:
var serviceRoot = "http://demos.telerik.com/kendo-ui/service";
angular.module("KendoDemos", ["kendo.directives"])
.controller("MyCtrl", function($scope, $http) {
$scope.treeData = new kendo.data.HierarchicalDataSource({
data: [{
text: "Cat"
}, {
text: "Dog",
items: [{
text: "Fido"
}, {
text: "Rover"
}]
}, {
text: "Rabbit",
checked: true
}]
});
});
(function() {
'use strict';
var app = angular.module('KendoDemos');
var template = '<div> <div class="input-group"> <input type="text" ng-click="textNotFound=false" class="form-control" placeholder="Find node" ng-model="searchText" ng-enter="search()" ng-esc="searchText='''';textNotFound = false;"> <div class="input-group-btn"> <span class="btn btn-default" ng-click="search()"><span class="fa fa-search clickable" style="font-size: 14px; height: 18px"></span></span> </div> </div> <div id="treeview" kendo-tree-view="searchTree" k-data-source="dataSource" k-load-on-demand="false" k-on-check="onCheck(kendoEvent)" k-options="{checkboxes:true }"> <span k-template>{{dataItem.text}}</span> </div></div>'
app.directive('searchableTree', function() {
//Usage:
//<div data-searchable-tree ng-model="vm.treeData"></div>
var directive = {
template: template,
require: '?^form',
replace: true,
transclude: true,
scope: {
'dataSource': '=ngModel',
'controlId': '@id'
},
restrict: 'AE',
link: function($scope, element, attrs, formCtrl) {
$scope.search = function(id) {
var tree = $scope.searchTree;
var node = tree.findByText($scope.searchText);
tree.expandTo($scope.searchTree.dataItem(node));
tree.select(node);
tree.dataItem(node).set("checked", true);
//var checkbox = $(node).find(":checkbox");
//checkbox.prop("checked", true);
}
$scope.setSelected = function(id) {
alert(id);
}
$scope.onSelect = function(id) {
alert(id);
}
$scope.onCheck = function(e) {
var checkbox = $(e.node).find(":checkbox");
var checked = checkbox.prop("checked");
//updateValidity(e.node, checked);
}
}
};
return directive;
});
app.directive('ngEnter', function() {
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if (event.which === 13) {
scope.$apply(function() {
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
app.directive('ngEsc', function() {
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if (event.which === 27) {
scope.$apply(function() {
scope.$eval(attrs.ngEsc);
});
event.preventDefault();
}
});
};
});
})();
/* Styles go here */
html {
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
}
#example {
text-align: center;
}
.demo-section {
display: inline-block;
vertical-align: top;
width: 320px;
height: 300px;
text-align: left;
margin: 0 2em;
}
.clickable {
cursor: pointer;
}
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.default.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.dataviz.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.dataviz.default.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1411/styles/kendo.default.mobile.min.css" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="http://cdn.kendostatic.com/2014.3.1411/js/jquery.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.3.1411/js/angular.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.3.1411/js/kendo.all.min.js"></script>
<body>
<div id="example" ng-app="KendoDemos" ng-controller="MyCtrl">
<form name='myForm'>
<div name='myTree' data-searchable-tree ng-model="treeData"></div>
</form>
<hr/>
<div>Directive is {{myForm.myTree.$dirty ? 'dirty' : 'pristine'}}</div>
<hr/>
<div>Form is {{myForm.$dirty ? 'dirty' : 'pristine'}}</div>
</div>
</body>
2016年8月2日附录:自从写这篇文章以来,我意识到scope方法只在调试模式下可用。我不建议将其用于生产代码。
我也遇到过类似的问题,实际上我想出了一个非常简单的解决方案来解决它。如果你引用了一个元素,有一个scope()
方法可以用来查找它所在的范围
function dirtyParent(uid)
{
var formEl = angular.element(angular.element('#' + uid).closest('form'));
var formScope = formEl.scope();
var formName = formEl.attr('name');
formScope[formName].$setDirty();
}
这个函数从DOM中获取元素,找到它最近的祖先,即一个表单,获取该元素的名称和作用域,然后在作用域上找到表单控制器(它与表单的name
属性共享其名称),并在其上调用$setDirty()
方法。这样,您的控制器将被设置为$dirty状态。
在NewDev的回答中,您似乎可以使用$formController信息跳过DOM遍历,但我在编写这段代码时并没有意识到这一点。
这里有一些事情,所以在不讨论特定指令的细节的情况下,您应该理解以下内容:
ng模型:
您应该require: "ngModel"
,而不是将其用作范围变量。ng-model
是一个指令,通过它可以正确地与其他表单和验证器集成。ng-model
是DOM无关的,应该用作模型和视图值之间的概念管道。
如果操作得当,就不需要require: "form"
。
您有一个自定义输入控件:
认识到您正在有效地构建自定义输入指令。它以某种方式设置模型,并以某种方式呈现UI。
看看Angular文档中关于创建自定义输入指令的示例。
在自定义指令的模板中使用ng模型:
现在,您看到的$dirty
的问题是,指令(ng-model="searchText"
)中的ng-model
与外部表单集成,完全没有意识到它是另一个自定义输入控件的一部分。
ngModel
指令在DOM层次结构中搜索(请参见src)一个表单指令,最终找到指令所在的表单。
处理此问题的一种方法是,不要在模板中使用ng-model
,而是执行element.on('input')
(或其他操作)来检测更改,并适当地设置"必需"ngModel
的视图值。
另一种是过于"欺骗"ngModel
,使其相信表单控制器为空,因此不会更新它
这很容易做到,但我不能评论这是否经得起未来的考验。在指令的pre-link
中执行以下操作:
link: {
pre: function(scope, element){
// this will trip the search, and apply a `nullFormCtrl` internally,
// which doesn't do anything.
element.data("$formController", null);
}
}
- 使用 Pure JS 更改 HTML 标记名称
- 如何正确控制Angular指令的$dirty和$pure状态
- 像 exports.default = (0, _createHelper2.default)(pure, 'pure'
- consume salesforce chatter rest service from pure javascript
- Pure JavaScript alternative to jQuerys .find
- 如何在自定义指令中将字段设置为$dirty
- $dirty上的角度变化类
- 如何仅在字段为$dirty时应用ng模式
- Pure Javascript Sandbox
- Pure JS to lodash
- Jquery ONE function PURE js
- CKEditor检查源编辑区上的Dirty()
- 构建一个移动友好的浏览器游戏 - Pure JS,Jquery,Flash,Unity或WebGL
- 使用 PURE JS 基于输入标签添加占位符属性 - 没有 jQuery
- 如何在 Pure Jquery 中验证表单数组
- Fusion Charts, from flash to pure javascript
- Pure WebGL Dashed Line
- 在 Plain/Pure Javascript 中,你如何找出元素对象具有哪些可用的方法或属性
- Pure.js 必须与现有页面节点一起使用
- AngularJS表单保持$dirty手动设置每个输入$pristine