观察嵌入输入中 $pristine 或 $touched 的变化
Watching for changes in $pristine or $touched in transcluded input
我正在尝试围绕一个输入元素构建一个指令,该指令在模型变脏或被触摸时做出响应。所需的ngModel似乎反映了输入模型的值和视图的变化,但其他属性的none。
我怀疑这与我将 ng-model 包含在两个元素中有关,但我还没有想出如何使用它一次。
理想情况下,我想要这样创建的东西:
<input test-directive label="'My Label'" type="text" ng-model="testObject.text"/>
结果如下:
<label>
<div>My Label</div>
<input ng-model="testObject.text" ng-blur="input.focus=false" ng-focus="input.focus=true"/>
Focused: true (input.focus)
Pristine: false (ngModel.$pristine)
</label>
这是我目前的情况:fiddle
<div test-directive ng-model="testObject.text" l="'Test Input'" f="testObject.focus">
<input type="text" ng-model="testObject.text" ng-blur="testObject.focus=false" ng-focus="testObject.focus=true" />
</div>
指令监视 ngModel。
app.directive('testDirective', ['$compile',
function ($compile) {
'use strict';
return {
restrict: 'A',
require: "ngModel",
scope: {
l: '=',
f: '='
},
link: function (scope, element, attr, ngModel) {
var input = element.find('input');
scope.$watch(function () {
return ngModel;
}, function (modelView) {
scope.modelView = modelView
});
},
template:
'<div>' +
'<label>' +
'{{l}}' +
'<div class="iwc-input" ng-transclude></div>' +
'</label>' +
'focus: {{f}}' +
'<pre>{{modelView|json}}</pre>' +
'</div>',
transclude: true,
replace: false
};
}]);
我发现在 Angular 中有一个指令 "self-wrap" 是相当复杂的,同时仍然有其他指令与它一起正常工作。所以,下面的答案有效,我将尝试解释为什么它比应该的更复杂。
有多种方法可以解决这个问题。我将使用带有 transclude: "element"
的方法 - 这将包含整个元素并允许您将其放置在任何地方(包括换行)。
.directive("wrapper", function($compile){
return {
scope: { l: "@" },
transclude: "element",
require: ["ngModel"],
link: function(scope, element, attrs, ctrls, transclude)
scope.ngModel = ctrls[0];
// transclude: "element" ignores the template property, so compile it manually
var template = '<label ng-class="{dirty: ngModel.$dirty}">{{l}}: \
<placeholder></placeholder>\
</label>';
$compile(template)(scope, function(prelinkedTemplate){
transclude(function (clonedElement){
prelinkedTemplate.find("placeholder").replaceWith(clonedElement);
// element here is only a comment after transclusion
// so, need to use .after() - not .append()
element.after(prelinkedTemplate);
});
})
}
})
因此,上面的代码编译了模板并链接到隔离范围(其中 $scope.l
和 $scope.ngModel
可用),然后排斥元素并替换 <placeholder>
。
这应该已经足够了,但是有一个问题。当 Angular 编译我们的指令时,该元素已被嵌入并且现在是注释 <!-- wrapper -->
,而不是 <input>
- 这就是 ngModel
指令 "sees" 在其预链接功能,所以事情开始中断。
要修复,我们的指令需要比 ngModel
(即 1)具有更高的优先级,事实上,对于诸如 ng-maxlength
去上班。但如果我们这样做,那么我们就不能 require: "ngModel"
,因为它在我们的优先级还不可用。
解决此问题的一种方法是进行 2 次传递 - 一次优先级较高,一次优先级较低。优先级较低的传递会将 "hang" 捕获的 ngModel
控制器传递给指令的控制器。方法如下:
// first pass
app.directive("wrapper", function($compile) {
return {
priority: 101,
scope: {
l: "@"
},
transclude: "element",
controller: angular.noop, // just a noop controller to attach properties to
controllerAs: "ctrl", // expose controller properties as "ctrl"
link: function(scope, element, attrs, ctrls, transclude) {
// notice the change to "ctrl.ngModel"
var template = '<label ng-class="{dirty: ctrl.ngModel.$dirty}">{{l}}: \
<placeholder></placeholder>\
</label>';
$compile(template)(scope, function(prelinkedTemplate) {
transclude(function(clonedElement) {
prelinkedTemplate.find("placeholder").replaceWith(clonedElement);
element.after(prelinkedTemplate);
});
});
}
};
})
// second pass
.directive("wrapper", function($compile) {
return {
priority: -1,
require: ["wrapper", "ngModel"],
link: function(scope, element, attrs, ctrls, transclude) {
var wrapperCtrl = ctrls[0],
ngModel = ctrls[1];
// "hang" ngModel as a property of the controller
wrapperCtrl.ngModel = ngModel;
}
};
});
还有其他方法。例如,我们可以使该指令具有非常高的优先级(例如 priority: 10000
)和 terminal: true
。然后,我们可以获取元素,将其包装,应用另一个具有 require: "ngModel"
的指令来实际跟踪 $pristine
、$touched
等...并重新编译内容(不要忘记删除原始指令以避免无限循环)。
我正在尝试围绕一个输入元素构建一个指令,该指令在模型变脏或被触摸时做出响应。所需的ngModel似乎反映了输入模型的值和视图的变化,但其他属性的none。
我怀疑这与我将 ng-model 包含在两个元素中有关,但我还没有想出如何使用它一次。
理想情况下,我想要这样创建的东西:
<input test-directive label="'My Label'" type="text" ng-model="testObject.text"/>
结果如下:
<label>
<div>My Label</div>
<input ng-model="testObject.text" ng-blur="input.focus=false" ng-focus="input.focus=true"/>
Focused: true (input.focus)
Pristine: false (ngModel.$pristine)
</label>
这是我目前的情况:fiddle
<div test-directive ng-model="testObject.text" l="'Test Input'" f="testObject.focus">
<input type="text" ng-model="testObject.text" ng-blur="testObject.focus=false" ng-focus="testObject.focus=true" />
</div>
指令监视 ngModel。
app.directive('testDirective', ['$compile',
function ($compile) {
'use strict';
return {
restrict: 'A',
require: "ngModel",
scope: {
l: '=',
f: '='
},
link: function (scope, element, attr, ngModel) {
var input = element.find('input');
scope.$watch(function () {
return ngModel;
}, function (modelView) {
scope.modelView = modelView
});
},
template:
'<div>' +
'<label>' +
'{{l}}' +
'<div class="iwc-input" ng-transclude></div>' +
'</label>' +
'focus: {{f}}' +
'<pre>{{modelView|json}}</pre>' +
'</div>',
transclude: true,
replace: false
};
}]);
我发现在 Angular 中有一个指令 "self-wrap" 是相当复杂的,同时仍然有其他指令与它一起正常工作。所以,下面的答案有效,我将尝试解释为什么它比应该的更复杂。
有多种方法可以解决这个问题。我将使用带有 transclude: "element"
的方法 - 这将包含整个元素并允许您将其放置在任何地方(包括换行)。
.directive("wrapper", function($compile){
return {
scope: { l: "@" },
transclude: "element",
require: ["ngModel"],
link: function(scope, element, attrs, ctrls, transclude)
scope.ngModel = ctrls[0];
// transclude: "element" ignores the template property, so compile it manually
var template = '<label ng-class="{dirty: ngModel.$dirty}">{{l}}: \
<placeholder></placeholder>\
</label>';
$compile(template)(scope, function(prelinkedTemplate){
transclude(function (clonedElement){
prelinkedTemplate.find("placeholder").replaceWith(clonedElement);
// element here is only a comment after transclusion
// so, need to use .after() - not .append()
element.after(prelinkedTemplate);
});
})
}
})
因此,上面的代码编译了模板并链接到隔离范围(其中 $scope.l
和 $scope.ngModel
可用),然后排斥元素并替换 <placeholder>
。
这应该已经足够了,但是有一个问题。当 Angular 编译我们的指令时,该元素已被嵌入并且现在是注释 <!-- wrapper -->
,而不是 <input>
- 这就是 ngModel
指令 "sees" 在其预链接功能,所以事情开始中断。
要修复,我们的指令需要比 ngModel
(即 1)具有更高的优先级,事实上,对于诸如 ng-maxlength
去上班。但如果我们这样做,那么我们就不能 require: "ngModel"
,因为它在我们的优先级还不可用。
解决此问题的一种方法是进行 2 次传递 - 一次优先级较高,一次优先级较低。优先级较低的传递会将 "hang" 捕获的 ngModel
控制器传递给指令的控制器。方法如下:
// first pass
app.directive("wrapper", function($compile) {
return {
priority: 101,
scope: {
l: "@"
},
transclude: "element",
controller: angular.noop, // just a noop controller to attach properties to
controllerAs: "ctrl", // expose controller properties as "ctrl"
link: function(scope, element, attrs, ctrls, transclude) {
// notice the change to "ctrl.ngModel"
var template = '<label ng-class="{dirty: ctrl.ngModel.$dirty}">{{l}}: \
<placeholder></placeholder>\
</label>';
$compile(template)(scope, function(prelinkedTemplate) {
transclude(function(clonedElement) {
prelinkedTemplate.find("placeholder").replaceWith(clonedElement);
element.after(prelinkedTemplate);
});
});
}
};
})
// second pass
.directive("wrapper", function($compile) {
return {
priority: -1,
require: ["wrapper", "ngModel"],
link: function(scope, element, attrs, ctrls, transclude) {
var wrapperCtrl = ctrls[0],
ngModel = ctrls[1];
// "hang" ngModel as a property of the controller
wrapperCtrl.ngModel = ngModel;
}
};
});
还有其他方法。例如,我们可以使该指令具有非常高的优先级(例如 priority: 10000
)和 terminal: true
。然后,我们可以获取元素,将其包装,应用另一个具有 require: "ngModel"
的指令来实际跟踪 $pristine
、$touched
等...并重新编译内容(不要忘记删除原始指令以避免无限循环)。