angular 除非使用超时,否则通过 $http 传输的对象与之前的输出不同

angular object transferred via $http differs from beforehand output unless timeout is used

我创建了一个带有 2 向对象数据绑定的嵌套指令结构。 在 js 方面,一切都像一个魅力,直到我将它连接到我的服务,将数据发送到我的 api。 因为在 post 请求中对象有旧值,它总是忽略最新的变化。但真正的问题是......在我将对象传递给请求之前输出对象,它具有正确的更新值。

service.updatePerson = function(person) {
        console.log(person); //this outputs the correct up to date data
        $http.post(Routing.generate('update_person'), person); // this works not

        //After trying some more (see update below) - this works as well
        $timeout(function() {
            $http.post(Routing.generate('update_person'), person);
        }, 1000);
};

(我正在使用 symfony 的 fos 路由包)

使用我的开发人员工具检查请求,我可以看到提交的对象具有旧值。但它不像值永远不会改变,它总是最后一次但不是当前的变化。

我是 angular 的新手,可能会忽略 $http 服务的一些特殊之处 - 我没有启用缓存,所以这应该不是问题所在。 此外,问题仅发生在更新的对象上,如果我发送一个完整的新实体则不会。

我能看到并且我怀疑会导致问题的唯一区别是嵌套的深度。 我有一个名为 'collapsedTable' 的指令,它接受不同的数据。不仅是数据,更新、添加和删除方法也被传递给指令。

这是指令的用法:

<collapsed-table ng-if="formModel" ng-if="persons" view-data="persons" form-model="formModel" add-object="newPerson" add-item="addPerson(item)"  update-item="updatePerson(item)" delete-item="deletePerson(item)"></collapsed-table>

直接在指令中添加和删除项目:

<button href="#" class="btn btn-success pull-right" ng-click="addItem({item : newItem})">
          Add
</button>

(删除按钮看起来一样,但是调用了删除功能)

但是更新数据不是通过一个按钮发生的,它直接绑定到表单字段并且表单字段是它自己的指令:

<form-field ng-if="column.type" form-field-bind="item[column.name]" form-field-action="updateItem({item:item})" form-field-action-param="item" form-select-options="column.data" form-template="{{column.type}}"></form-field>

(我知道,这里发生的一般狗屎)

并在指令中(例如 editableTextarea):

<a href="#" onaftersave="formFieldAction({item : formFieldActionParam})" editable-textarea="$parent.formFieldBind">{{formFieldBind || 'eintragen'}}</a>

(此处使用 angular 的 x-editable 模块)

我认为 post 阅读我的指令的全部内容太过分了。但我认为范围设置与理解函数和变量的传递方式有关。

collapsedTableDirective.js

.directive('collapsedTable',['$filter',function($filter) {
    return {
        restrict: 'E',
        templateUrl: 'templates/collapsedTableView.html',
        scope: {
            data: "=viewData",
            newItem: '=addObject',
            formModel: '=',
            deleteItem: '&',
            updateItem: '&',
            addItem: '&'
        }
    }
}]);

formFieldDirective.js

.directive('formField',['formTemplateFactory', '$filter',function(formTemplateFactory, $filter) {
    return {
        restrict: 'E',
        scope: {
            formFieldBind: '=',
            formFieldActionParam: '=',
            formFieldAction: '&',
            formTemplate: '@',
            formSelectOptions: '='
        },
        transclude: true,
        template: '<div ng-include="getTemplate()"></div>'
    }
}]);

我还展示了表单字段模板是通过 ng-include 提取的,它创建了一个新的范围,这就是为什么在表单字段模板中使用 "parent" 引用绑定变量的原因。

但是对于所有可能在这里讨论的问题,请不要忘记:

console.log 在服务中输出正确的数据。只有在请求中我们有旧数据。这也是 ist 有点难以调试的原因......从调试器方面来看,我的对象总是看起来很好。

感谢任何帮助!

更新 - $timeout 有效

尝试了一些东西之后,我有了尝试 timeout 的想法(来自完全不相关的 post)。是的,超时可以解决问题。 但我没有回答这个问题,因为我真的没有对这种行为的解释。 console.log 不需要超时,尽管它应该在 post 请求之前执行。 此外,可能还有比使用超时更合适的解决方案。

personservice.updatePerson 运行时没有更新,看起来它是由不适应摘要周期的非 Angular 代码触发的。在这种情况下,这个

    $scope.$apply();
    // or $rootScope.$apply() if this is a service and not a controller
    $http.post(Routing.generate('update_person'), person);

可能会有所帮助,尽管在调用者函数中执行 $apply 而不是被调用者始终是一个更好的习惯,因此外部 JS 应该触发包装器:

scope.externalAction = function (obj) {
  scope.$apply(function () {
    scope.formFieldAction(obj);
  };
};

console.log 不输出当时实际的数据,因为它是由对象引用提供的,它可能反映了调用 console.log 之后发生的变化。如果考虑到这一点,这是一个有用的功能,否则就是有害的。

要记录当前对象状态总是使用

console.log(JSON.stringify(person));