从$ scope函数调用dom更新方法的最佳位置在哪里?

时间:2013-12-24 07:59:23

标签: javascript jquery angularjs

我有一个表单,其中的部分滚动并在用户与之交互时自动排列。我希望在指令中定义所有逻辑,但目前无法弄清楚如何从控制器中获取一些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操作逻辑的标准模式?

2 个答案:

答案 0 :(得分:1)

HTML使用名称锚点处理页面内的滚动。 <a name="sectionX"><a href="#sectionX">如果使用路由器,这些内容会在SPA中被大量使用(错误)。

范围/控制器不知道dom并且不能/不应该更改它。 The FAQ说:

  

DOM操作

     

停止尝试使用jQuery修改控制器中的DOM。真。   这包括添加元素,删除元素,检索它们   内容,显示和隐藏它们。使用内置指令,或写   你自己的必要,做你的DOM操作。见下文   复制功能。

有人编写了an ngScrollTo directive,它将逻辑保留在view +指令中。我没试过,但它看起来像是要走的路。

另请参阅Anchor links in Angularjs?了解替代解决方案。

答案 1 :(得分:0)

  

将一个函数附加到这样的DOM对象,这样我的所有DOM操作都可以包含在指令

这里简短的回答是不,不是真的。如果控制器具有业务逻辑,那么它不应该关心DOM中发生的事情。

  

是否存在触发控制器指令中定义的DOM操作逻辑的标准模式?

不确定它们是否标准,但它们有几种方式。他们的共同主题是直接或通过服务处理业务逻辑的控制器实际上并不调用指令,或者真正知道DOM /视图中发生了什么。它只是以一种或另一种形式提供“钩子”,因此指令可以做出适当的反应。

我所知道的方式是:

  1. 对范围内变量的更改做出反应。所以你可以有一个变量,比如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?
       });
    }
    
  2. 从控制器对事件$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
       });
    }
    
  3. 对来自控制器的事件$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.
       });
    }
    
  4. 您说您正在使用表单。您可以创建一组交互的自定义​​指令,就像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
      };
    }
    
  5. 我意识到上述方法并不是特别针对你的滚动问题:它们更通用,说实话,我还不确定在任何特定情况下推荐哪一种。