Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angularjs 如何获得模型更改(从指令中)以在视图中更新$申请_Angularjs_Angularjs Directive_Angularjs Scope - Fatal编程技术网

Angularjs 如何获得模型更改(从指令中)以在视图中更新$申请

Angularjs 如何获得模型更改(从指令中)以在视图中更新$申请,angularjs,angularjs-directive,angularjs-scope,Angularjs,Angularjs Directive,Angularjs Scope,在stackoverflow和google上都有很多关于这个主题的问题/答案($apply),我觉得我已经阅读了每一个问题/答案并进行了跟踪,但都没有用。我在谷歌上的所有搜索现在都返回紫色链接 以下是我面临的问题(尽量具体而不过分): 我有一个应用程序,它通过Web服务提取数据,将记录存储在一个数组中。我已经创建了一个拖放目标,以上载一个excel文件,其中包含对数据的更改/添加。我已经为drop目标创建了一个指令,它将事件侦听器绑定到元素。我已经隔离了作用域,使用&将一个函数从控制器绑定到指令

在stackoverflow和google上都有很多关于这个主题的问题/答案(
$apply
),我觉得我已经阅读了每一个问题/答案并进行了跟踪,但都没有用。我在谷歌上的所有搜索现在都返回紫色链接

以下是我面临的问题(尽量具体而不过分):

我有一个应用程序,它通过Web服务提取数据,将记录存储在一个数组中。我已经创建了一个拖放目标,以上载一个excel文件,其中包含对数据的更改/添加。我已经为drop目标创建了一个指令,它将事件侦听器绑定到元素。我已经隔离了作用域,使用
&
将一个函数从控制器绑定到指令中。控制器中的函数处理删除的文件并更新模型。以下是设置:

HTML

<div ng-controller="myController as vm">
  <div class="row">
    <div class="col-lg-12">
      <div drop-target drop="vm.drop(files)">
        <p>Drag an XLSX file here to import.</p>
      </div>
    </div>
  </div>
</div>
指令

app.directive('dropTarget', function () {
    return {
        restrict: 'A',
        scope: {
            drop: '&'
        },
        link: function (scope, el, attrs, controller) {
            el.bind("dragover", function (e) {
                ...
            });

            el.bind("dragenter", function (e) {
                ...
            });

            el.bind("dragleave", function (e) {
                ...
            });

            el.bind("drop", function (e) {
                if (e.preventDefault) { e.preventDefault(); } // Necessary. Allows us to drop.
                if (e.stopPropogation) { e.stopPropogation(); } // Necessary. Allows us to drop.
                var files = e.originalEvent.dataTransfer.files;
                scope.$apply(function () {
                    scope.drop({ files: files });
                });
            });
        }
    }
});
scope.drop({ files: files }).then(function (r) {
    // Do nothing here, but doesn't work without .then
});
因此,我在网上读到的所有内容似乎都表明,我应该将对控制器函数的调用包装在
$apply()
中,正如您所看到的那样。拖放和更新模型的所有功能都可以正常工作。但是,该视图不会更新。模型已经更新——我可以在控制台中看到它。当我触发任何其他角度活动(单击具有ng click的按钮或选中具有ng change的复选框等)时,整个UI都会更新,我可以看到模型的所有更新

如果我在控制器中用$apply()包装对
importLines
的调用,效果会很好。但我的理解是$apply()调用应该在指令中完成……尽量避免在控制器中使用它

我一辈子都搞不明白为什么它不起作用。它似乎遵循了我在论坛和博客上读到的几十个关于$apply()的例子。我对angular还是相当陌生——我知道当一些论坛/博客开始讨论$digest循环之类的时候,我还没有完全理解它们(但我知道的比几天前多得多)。我知道对模型的一些更改是在angular的上下文之外完成的,因此您必须调用$apply()。一些答案说指令的链接函数在angular的上下文中,其他答案是否定的。我没有得到任何错误(在$apply中没有$apply)。一切都运行顺利…只是无法更新视图

我错过了什么?任何帮助都将不胜感激。

更新: 谢谢你的回答。最后,我在
vm.drop
函数中加入了
$q
承诺,在
导入行
函数完成后解决它。在指令中,当我离开
作用域$apply()
时,我得到一个错误,即$apply已经在处理中。但是我仍然必须在函数调用上有一个
。然后
,即使它是空的。没有它,它就不会正常工作

控制器

app.controller('myController', ['dataService', '$scope', function (data, $scope) {
    var vm = this;
    vm.data = data;
    vm.drop = function (files) {
        var reader = new FileReader();
        reader.onload = function (e) {
            ... Read and parse excel sheet into array of objects ...

            importLines(wbJson);  //call function to update model with objects
        };
        reader.readAsBinaryString(files[0]);
    }

    function importLines(lines) {
        //do a bunch of validation and update model data
    }
}
app.controller('myController', ['dataService', '$scope', function (data, $scope) {
    var vm = this;
    vm.data = data;
    vm.drop = function (files) {
        var deferred = $q.defer(); //Added
        var reader = new FileReader();
        reader.onload = function (e) {
            ... Read and parse excel sheet into array of objects ...

            importLines(wbJson);
            deferred.resolve(); //Added
        };
        reader.readAsBinaryString(files[0]);
        return deferred.promise; //Added
    }

    function importLines(lines) {
        //do a bunch of validation and update model data
    }
}
指令

app.directive('dropTarget', function () {
    return {
        restrict: 'A',
        scope: {
            drop: '&'
        },
        link: function (scope, el, attrs, controller) {
            el.bind("dragover", function (e) {
                ...
            });

            el.bind("dragenter", function (e) {
                ...
            });

            el.bind("dragleave", function (e) {
                ...
            });

            el.bind("drop", function (e) {
                if (e.preventDefault) { e.preventDefault(); } // Necessary. Allows us to drop.
                if (e.stopPropogation) { e.stopPropogation(); } // Necessary. Allows us to drop.
                var files = e.originalEvent.dataTransfer.files;
                scope.$apply(function () {
                    scope.drop({ files: files });
                });
            });
        }
    }
});
scope.drop({ files: files }).then(function (r) {
    // Do nothing here, but doesn't work without .then
});

它现在马上更新。谢谢你的帮助

我希望FileReader类是异步的。它在JS中阻塞是没有意义的。因此,这将解释为什么scope.apply不在指令中工作。原因是:作用域应用在
importLines
有机会运行之前运行(因为它是异步的)

您可以查看
$q
(一个promise对象,它将很好地解决这个问题),或者您可以创建一个回调函数,它将“通知”指令importline最终完成

vm.drop = function (files, doneCallback) {
    var reader = new FileReader();
        reader.onload = function (e) {
          ... Read and parse excel sheet into array of objects ...

          importLines(wbJson);  //call function to update model with objects
          if(doneCallback) 
            doneCallback();
        };
        reader.readAsBinaryString(files[0]);
}
在指令中:

  scope.drop({ files: files }, scope.$apply);

指令可以具有独立的作用域,也可以与父作用域共享该作用域。然而,在您的例子中,您通过声明

scope: {
  drop: '&'
},
在你的指令中,允许指令的隔离作用域将值传递到父作用域,以便在属性中定义的表达式中进行计算。相反,=在指令的隔离作用域和父作用域之间设置双向绑定表达式。如果你利用

scope: {
  drop: '='
}, 
在您的指令中,它将完全传递该范围对象,并使用双向绑定

您可以在指令中使用ngModel

app.directive('dropTarget', function () {
  return {
    restrict: 'A',
    require:'^ngModel',
    link: function (scope, el, attrs, controller,ngModel) {
      el.bind("dragover", function (e) {
        ...
      });

      el.bind("dragenter", function (e) {
        ...
      });

      el.bind("dragleave", function (e) {
        ...
      });

      el.bind("drop", function (e) {
        if (e.preventDefault) { e.preventDefault(); } // Necessary. Allows us to drop.
        if (e.stopPropogation) { e.stopPropogation(); } // Necessary. Allows us to drop.
        var files = e.originalEvent.dataTransfer.files;
        scope.$apply(function () {
          ngModel.$setViewValue({ files: files });
        });
      });
    }
  }
});