javascript - Angularjs watch 无法处理对象内部的数组

标签 javascript angularjs arrays

我有一个对象 {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 calling ctrl.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/

相关文章:

javascript - 将项目插入数组的最佳性能方法?

arrays - 如何在多维数组中查找邻居?

javascript - AngularJS:从作用域调用 $watchGroup

javascript - 向 requestAnimationFrame 回调的函数添加额外的参数

javascript - 将 TypeScript 与 Jest 一起使用不会捕获语法错误

javascript - 从隐藏输入中的值输出 JSON

javascript - 如何附加到 Firefox 附加组件中的文件?

javascript - 将 $mdDialog 添加到 Angular 函数

javascript - ng-repeat 在两行(或更多行)上

javascript - AngularJS:初始化 ZURB Foundation JS