AngularJS: 在解决承诺后更新表单字段的代码属于哪里?
AngularJS: Where does code belong that updates a form field after a promise is resolved?
在我的 AngularJS 应用程序中,我有一个包含 <textarea>
和提交按钮的模板。 <div>
(可以很容易地成为 <form>
)包装这两个元素并具有控制器。单击提交按钮时,控制器使用 $resource 服务将 <textarea>
中的值 POST
转换为 REST API。
模板:
<div ng-controller="MyController as vm">
<textarea ng-model="vm.text" rows="5"></textarea>
<button ng-click="vm.save()">Save</button>
</div>
控制器:
angular.module('myApp')
.controller('MyController', MyController);
MyController.$inject = ['myRestApiResource'];
function MyController(myRestApiResource) {
var vm = this;
vm.save = function() {
var params = [];
var postData = {text: vm.text};
myRestApiResource.save(params, postData)
.then(function(res) { /* success handler */ })
.catch(function(res) { /* error handler */ });
};
}
当资源 save()
方法的 $promise 被拒绝时,响应数据(在某些情况下)包含一个 error.position
属性 引用字符串索引,其中vm.text
.
中检测到语法错误
在那些情况下,我关注 <textarea>
元素和从 error.position
到下一个非单词字符的 set the selection range。我有一个功能,一切正常。
我的问题是:
是否应该在<textarea>
(承诺被拒绝后)上执行设置选择范围的函数调用...
- 控制器
- 添加到文本区域的自定义指令
- 其他地方
...为什么?
这是一个关于关注点分离的问题。
控制器似乎是最明智的地方,但我的理解是控制器不应该执行 DOM 操作......这需要使用 $element
服务(或 jQuery) 就是为了这样做。
自定义指令需要订阅控制器广播的事件,然后设置自己的焦点和选择范围。这分开了关注点,但根据 Angular Best Practices $broadcast
和 $on
应该只用于 "events that are relevant globally across the entire app"。
我想控制器和指令可以通过另一个服务进行通信,但这似乎是过度设计——这是我臭名昭著的,并且在这里努力避免! :-)
提前致谢!
解决方案
为了完整起见,这是我根据@kristin-fritsch 的回答编写的自定义指令:
指令
angular.module('myApp')
.directive('selectionRange', selectionRangeDirective);
function selectionRangeDirective() {
return {
restrict: 'A',
scope: {
selectionRange: '='
},
link: function(scope, iElement) {
var element = iElement[0];
if (element.setSelectionRange && typeof element.setSelectionRange === 'function') {
scope.$watch('selectionRange', function(range) {
if (range && typeof range.start === 'number' && range.start > -1 && range.start < iElement.val().length) {
element.focus();
element.setSelectionRange(range.start, range.end);
}
});
}
}
};
}
为了实现它,我将指令属性添加到我的文本区域:
<textarea ng-model="vm.text" rows="5" selection-range="vm.selection"></textarea>
...并在我的控制器中将 vm.selection
的值设置为具有 start
和 end
属性的对象,当承诺被拒绝时。
myRestApiResource.save(params, postData)
.then(function(res) { /* success handler */ })
.catch(function(res) {
var error = res.data.error;
if (error && error.position) {
var start = error.position;
var end = start + vm.text.substr(start).search(/(\W|$)/);
vm.selection = {start: start, end: end};
}
});
我建议你在文本区域添加一个指令。
<div ng-controller="MyController as vm">
<textarea ng-model="vm.text" data-error-position="errorPosition" rows="5"></textarea>
<button ng-click="vm.save()">Save</button>
</div>
然后你可以像这样设置 $promise 被拒绝时的位置
$scope.errorPosition = 50;
并在 errorPosition - 指令中处理 DOM 操作。
在我的 AngularJS 应用程序中,我有一个包含 <textarea>
和提交按钮的模板。 <div>
(可以很容易地成为 <form>
)包装这两个元素并具有控制器。单击提交按钮时,控制器使用 $resource 服务将 <textarea>
中的值 POST
转换为 REST API。
模板:
<div ng-controller="MyController as vm">
<textarea ng-model="vm.text" rows="5"></textarea>
<button ng-click="vm.save()">Save</button>
</div>
控制器:
angular.module('myApp')
.controller('MyController', MyController);
MyController.$inject = ['myRestApiResource'];
function MyController(myRestApiResource) {
var vm = this;
vm.save = function() {
var params = [];
var postData = {text: vm.text};
myRestApiResource.save(params, postData)
.then(function(res) { /* success handler */ })
.catch(function(res) { /* error handler */ });
};
}
当资源 save()
方法的 $promise 被拒绝时,响应数据(在某些情况下)包含一个 error.position
属性 引用字符串索引,其中vm.text
.
在那些情况下,我关注 <textarea>
元素和从 error.position
到下一个非单词字符的 set the selection range。我有一个功能,一切正常。
我的问题是:
是否应该在<textarea>
(承诺被拒绝后)上执行设置选择范围的函数调用...
- 控制器
- 添加到文本区域的自定义指令
- 其他地方
...为什么?
这是一个关于关注点分离的问题。
控制器似乎是最明智的地方,但我的理解是控制器不应该执行 DOM 操作......这需要使用 $element
服务(或 jQuery) 就是为了这样做。
自定义指令需要订阅控制器广播的事件,然后设置自己的焦点和选择范围。这分开了关注点,但根据 Angular Best Practices $broadcast
和 $on
应该只用于 "events that are relevant globally across the entire app"。
我想控制器和指令可以通过另一个服务进行通信,但这似乎是过度设计——这是我臭名昭著的,并且在这里努力避免! :-)
提前致谢!
解决方案
为了完整起见,这是我根据@kristin-fritsch 的回答编写的自定义指令:
指令
angular.module('myApp')
.directive('selectionRange', selectionRangeDirective);
function selectionRangeDirective() {
return {
restrict: 'A',
scope: {
selectionRange: '='
},
link: function(scope, iElement) {
var element = iElement[0];
if (element.setSelectionRange && typeof element.setSelectionRange === 'function') {
scope.$watch('selectionRange', function(range) {
if (range && typeof range.start === 'number' && range.start > -1 && range.start < iElement.val().length) {
element.focus();
element.setSelectionRange(range.start, range.end);
}
});
}
}
};
}
为了实现它,我将指令属性添加到我的文本区域:
<textarea ng-model="vm.text" rows="5" selection-range="vm.selection"></textarea>
...并在我的控制器中将 vm.selection
的值设置为具有 start
和 end
属性的对象,当承诺被拒绝时。
myRestApiResource.save(params, postData)
.then(function(res) { /* success handler */ })
.catch(function(res) {
var error = res.data.error;
if (error && error.position) {
var start = error.position;
var end = start + vm.text.substr(start).search(/(\W|$)/);
vm.selection = {start: start, end: end};
}
});
我建议你在文本区域添加一个指令。
<div ng-controller="MyController as vm">
<textarea ng-model="vm.text" data-error-position="errorPosition" rows="5"></textarea>
<button ng-click="vm.save()">Save</button>
</div>
然后你可以像这样设置 $promise 被拒绝时的位置
$scope.errorPosition = 50;
并在 errorPosition - 指令中处理 DOM 操作。