我有一个对象 {Client:[],Employee:[],Product:[],Project:[],PayPeriod:[]}
,其中每个数组都由组件推送和拼接通过双向绑定(bind)。主 Controller 连接所有 5 个阵列并将它们提供给另一个组件。在所述组件中,我需要观察该绑定(bind),但无论我做什么,它都不起作用。这就是我现在拥有的。
$scope.$watch('ctrl.parameters', ctrl.Update(), true);
ctrl.Update();是一个函数并且有效。
ctrl.parameters 确实得到更新,但不会触发 $watch。
这有点复杂,所以如果你需要任何解释黄油我可以。
ctrl.Update = function () {
$.post("/TrackIt/Query.php?Type=getViaParams&EntityType="+ctrl.entity,{Params:ctrl.parameters},function(Data,Status){
if(Status=="success"){
if (Data.Success) {
ctrl.List = Data.Result.Entities;
} else {
AlertService.Alert(Data.Errors[0],false,null);
SessionService.Session(function () {
ctrl.Update();
});
}
$scope.$apply();
}else{
AlertService.Alert("Something is up with the select options",false,null);
}
},'json');
};
编辑1:
Par = {客户:[],员工:[],产品:[],项目:[],PayPeriod:[]}
5 个具有两种方式绑定(bind)的组件 = Par.X(这些是编辑参数的内容)
1 个具有双向绑定(bind)的组件 = Par(我需要观察这里的绑定(bind))
编辑2:
<script>
TrackIT.controller('EntryController', function EntryController($scope, $http, AlertService, SessionService, DisplayService) {
$scope.Parameters = {Client:[],Employee:[],Product:[],Project:[],PayPeriod:[]};
$scope.Values = {};
});
</script>
<style>
entity-select{
float: left;
display: inline;
padding: 0 5px;
}
#SelectParameters{
float: left;
}
</style>
<div ng-app="TrackIT" ng-controller="EntryController">
<div id="SelectParameters">
<entity-select entity="'Client'" ng-model="Values.Client" multi="true" ng-array="Parameters.Client"></entity-select>
<entity-select entity="'Employee'" ng-model="Values.Employee" multi="true" ng-array="Parameters.Employee"></entity-select>
<entity-select entity="'Product'" ng-model="Values.Product" multi="true" ng-array="Parameters.Product"></entity-select>
<entity-select entity="'Project'" ng-model="Values.Project" multi="true" ng-array="Parameters.Project"></entity-select>
<entity-select entity="'PayPeriod'" ng-model="Values.PayPeriod" multi="true" ng-array="Parameters.PayPeriod"></entity-select>
</div>
<br>
<parameter-table entity="'Entry'" parameters="Parameters"></parameter-table>
</div>
TrackIT.component('entitySelect', {
templateUrl: "/Content/Templates/Select.html",
controller: function SelectController($scope, $http, AlertService, SessionService) {
var ctrl = this;
ctrl.Options = [];
ctrl.Display = [];
ctrl.Add = function () {
var Display = {'Label':ctrl.Label(ctrl.ngModel),'Value':ctrl.ngModel};
ctrl.ngArray.push(ctrl.ngModel);
ctrl.Display.push(Display);
};
ctrl.Remove = function (Key) {
ctrl.ngArray.splice(Key, 1);
ctrl.Display.splice(Key, 1);
};
ctrl.$onInit = function() {
$.post("/TrackIt/Query.php?Type=getSelectList&EntityType="+ctrl.entity,null,function(Data,Status){
if(Status=="success"){
if (Data.Success) {
ctrl.Options = Data.Result.Entities;
if(ctrl.ngModel==undefined){
if(ctrl.none){
ctrl.ngModel = "NULL"
}else{
ctrl.ngModel = angular.copy(ctrl.Options[0].Attributes.ID.Value.toString());
}
}
} else {
AlertService.Alert(Data.Errors[0],false,null);
}
$scope.$apply();
}else{
AlertService.Alert("Something is up with the select options",false,null);
}
},'json');
};
ctrl.Label = function(Value) {
for (var prop in ctrl.Options) {
if(!ctrl.Options.hasOwnProperty(prop)) continue;
if(ctrl.Options[prop].Attributes.ID.Value.toString()==Value.toString()){
return ctrl.Options[prop].DisplayName;
}
}
};
},
bindings: {
entity:"<",
multi:"<",
none:"<",
ngModel:"=",
ngArray:"="
}
});
TrackIT.component('parameterTable', {
templateUrl: "/Content/Templates/BasicTable.html",
controller: function ParameterTableController($scope, $http, AlertService, SessionService, DisplayService) {
var ctrl = this;
ctrl.List = {};
ctrl.Update = function () {
$.post("/TrackIt/Query.php?Type=getViaParams&EntityType="+ctrl.entity,{Params:ctrl.parameters},function(Data,Status){
if(Status=="success"){
if (Data.Success) {
ctrl.List = Data.Result.Entities;
} else {
AlertService.Alert(Data.Errors[0],false,null);
SessionService.Session(function () {
ctrl.Update();
});
}
$scope.$apply();
}else{
AlertService.Alert("Something is up with the select options",false,null);
}
},'json');
};
$scope.$watch('ctrl.parameters', ctrl.Update.bind(ctrl), true);
ctrl.$onInit = function() {
DisplayService.DisplayTrigger(function () {
ctrl.Update();
});
ctrl.Update();
}
},
bindings: {
entity: "<",
parameters: "="
}
});
最佳答案
这里有两个问题。
问题1:ctrl
不是范围内的属性
看到完整的 Controller 代码后,我可以看到ctrl
只是 this
的别名,将在范围上发布为 $ctrl
的 Controller 实例默认情况下。但是您可以通过将函数而不是字符串传递给 $scope.$watch()
来避免担心它的名称。 :
// ES5
$scope.$watch(function () { return ctrl.parameters; }, ctrl.Update, true);
// ES6/Typescript/Babel
$scope.$watch(() => ctrl.parameters, ctrl.Update, true);
这是 Angular 的所有功能
你可能不知道,就 Angular 而言,它总是为每个 watch 调用一个函数来获取要比较的值。当您将字符串传递给 $scope.$watch()
时, Angular 使用 $parse
从该表达式创建一个函数。这就是 Angular 将字符串转换为绑定(bind)、表达式等中的可执行代码的方式。
创建的函数接受一个参数,该参数是计算表达式的“上下文”。您可以将其视为要使用的范围。
当您将函数传递给 $scope.$watch()
时作为第一个参数,您可以有效地避免 Angular 必须从字符串中为您创建一个函数。
问题2:指定watch监听函数的方式
您的ctrl.Update()
function 只是您希望在 ctrl.parameters
时运行的函数变化。
您在 $scope.$watch('ctrl.parameters', ctrl.Update(), true);
代码中所说的内容是:
Do a deep watch (watch changes to any property) on
ctrl.parameters
, and when it changes, call the result of callingctrl.Update()
, which will be a jQuery promise, not a function.
相反,您想传递 ctrl.Update
函数本身作为 $scope.$watch()
的第二个参数,因此当检测到更改时会调用它。为此,只需传递 ctrl.Update
而不是ctrl.Update()
:
$scope.$watch('ctrl.parameters', ctrl.Update, true);
注意事项
使用ctrl.Update
在这种特殊情况下会起作用,因为没有使用 this
在该函数内。对于查看此答案的其他人,请注意,当您以这种方式传递函数时,this
绑定(bind)(“上下文”)未维护为 ctrl
正如你所期望的那样。要解决此问题,请使用 ctrl.Update.bind(ctrl)
,或者只是将其包装在一个函数中,以便使用正确的上下文调用它:$scope.$watch('ctrl.parameters', function () { ctrl.Update() }, true);
.
谨慎使用深度/值(value) watch
您应该非常谨慎地在 Angular 应用程序中使用深度监视(也称为值(value)监视)。原因是,对于大对象来说,这是一个非常昂贵的操作,因为 Angular 必须在每个摘要周期上对对象进行深入比较 - 遍历整个对象上的每个属性,然后,如果有更改,则使对象的深度克隆,这又需要遍历每个属性来制作一个完全独立的副本,以便下次进行比较。
您可以将具有 n 个属性的对象上的深度监视视为相当于 n 个浅层/引用监视。
我有一种感觉,在你的情况下这个数字可能大得可怕。
关于javascript - Angularjs watch 无法处理对象内部的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39069567/