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