我有一个表单,其中的部分在用户与之交互时会自动滚动和排列。我希望在指令中定义所有逻辑,但目前还不知道如何从控制器中获取一些DOM操作逻辑。大多数函数都可以附加到滚动、点击或焦点事件上,但如果我的控制器中没有DOM逻辑,我如何将函数附加到我的作用域以触发一些DOM操作?
我目前拥有的是
$scope.scrollToNextSection = function(section){
//DOM manipulation logic to scroll to next section.
}
我有有效吗
directiveDOMObject.scrollToNextSection = function(section){
//DOM manipulation logic to scroll to next section.
}
然后用从我的控制器上调用它
$scope.scrollToNextSection = function(section){
directiveDOMObject.scrollToNextSection(section);
}
把一个函数附加到像这样的DOM对象上,这样我所有的DOM操作都可以包含在指令中吗?是否存在用于触发控制器指令中定义的DOM操作逻辑的标准模式?
HTML使用名称锚来处理页面内的滚动。<a name="sectionX">
和<a href="#sectionX">
如果你使用路由器,它们在SPA中会被大量(错误)使用。
作用域/控制器不知道dom,不能/不应该更改它。常见问题解答说:
DOM操作
停止尝试使用jQuery修改控制器中的DOM。真正地这包括添加元素、删除元素、检索它们内容,显示和隐藏它们。使用内置指令,或写入在必要时,您可以自己操作DOM。请参阅以下关于复制功能。
有人编写了ngScrollTo指令,该指令将逻辑保持在view+指令中。我还没有试过,但看起来还是不错的。
另请参阅Angularjs中的锚链接?寻找替代解决方案。
像这样将函数附加到DOM对象,这样我的所有DOM操作都可以包含在指令中
这里的简短回答是否定的,不是真的。如果控制器有业务逻辑,那么它就不应该关心DOM中发生的事情。
是否有标准模式用于触发控制器指令中定义的DOM操作逻辑?
不确定它们是否是标准的,但它们有几种方法。它们的共同主题是,直接或通过服务处理业务逻辑的控制器实际上并不调用指令,也不知道DOM/视图中发生了什么。它只是以一种或另一种形式提供"钩子",因此指令可以做出适当的反应。
据我所知,
-
对作用域上变量的变化做出反应。所以你可以有一个变量,比如
state
<div scroll-listen-to="state"> .... </div>
和一个指令
scrollListenTo
,具有如下范围+链接功能:scope: { scrollListenTo: '=' }, link: function postLink(scope, iElement, iAttrs) { scope.$watch('scrollListenTo', function(newValue, oldValue) { // Do something, maybe with scrolling? }); }
-
对来自控制器的事件
$broadcast
作出反应。这将事件发送到子作用域(因此发送作用域内的指令中的作用域)。此事件的名称也可以进行配置。因此,例如<div ng-controller="MyController"> <input scroller-event="MyController::stateChanged" /> </div>
然后在MyController中,在适当的点:
$scope.$broadcast('MyController::stateChanged', 'someData');
在指令中:
scope: { 'eventName': '@scrollerEvent' }, link: function postLink(scope, iElement, iAttrs) { scope.$on(scope.eventName, function(e, data) { // Do something the data }); }
-
对来自控制器的事件
$emit
作出反应。这与$broadcast
非常相似,但事件是通过层次结构向上发出的。您可以"包装"几个控制器,然后它们可以将事件发送到包装它们的指令。<div scroller-event="MyController::stateChanged"> <div ng-controller="MyController"> </div> <div ng-controller="MyController"> </div> </div>
然后在MyController 中
$scope.$emit('MyController::stateChanged', 'someData');
在这种情况下,您可能不应该在指令中使用
scope
参数,因为这会创建一个孤立的范围,而在这种情况中,这可能是不需要的。该指令可能有类似的内容link: function postLink(scope, iElement, iAttrs) { var eventName = iAttrs.scrollerEvent; scope.$on(eventName, function(e, data) { // Do something with the data, like scrolling. }); }
-
你说你在使用一个表单。你可以创建一组交互的自定义指令,就像ngModel和ngForm交互一样。例如,你可以有:
<div scroller-container> <input scroll-on-focus /> <input scroll-on-focus /> </div>
然后在
scrollOnFocus
指令require: '^scrollerContainer', link: function(scope, iElement, iAttrs, scrollerContainerController) { iElement.on('focus', function() { scrollerContainerController.scrollTo(iElement); }); }
在
scollerContainer
指令中,必须在其控制器上定义scrollTo
controller: function() { this.scrollTo = function(element) { // Some code that scrolls the container so the element is visible }; }
我意识到以上方法并不是特别针对你的滚动问题:它们更通用,老实说,我还不确定在任何特定情况下应该推荐哪种。