AngularJs :当有 "global" 时,更改 DOM 元素的最佳方法是什么?

AngularJs : what is the best way to changing DOM elements when there are "global"?

我来自 JQuery,与 AngularJs 的第一步让我有点困惑...

我的情况:
我有一个面板(#floatingpanel)隐藏在视口之外 css(顶部:-50px)。
我有一个 link (a.trigger),在我的应用程序的最深处,当你点击那个 link 时,我面板的“顶部”属性 从“- 50px”到“0px”以使其可见(您单击以切换可见性)。 该面板就在我页面主容器的这一端,但 link 它在其他数百个 div.

让我们看看DOM结构:

<body>
  <div class="main-wrapper" ng-app="myapp">
    <div class="app">
      <div class="many-things">
        [...]
        <a href="#" class="trigger" ng-click="toggleMyPanelVisibility()">
          Click here to see the panel
        </a>
        [...]
      </div>
    </div><!-- .app -->

    <div id="floatingpanel">
        Hey ! I was hidden !
    </div>

  </div> <!-- #mainwrapper -->
</body>

在jQuery中,它将是:

$('a.trigger').click( function(e) {
  e.preventDefault();
  var el = $('#floatingpanel');
  var top = el.css('top');
  if (top<0) el.css('top', 0);
   else  el.css('top', -50);
});

如何用 AngularJs 制作它?

我会那样做:

var app = angular.module('myapp');
app.run(["$rootScope", function($rootScope) {
    // Toggle du myaccountpanel
    $rootScope.floatingpanelVisibility = false;
    $rootScope.toggleMyPanelVisibility = function() {
        $rootScope.floatingpanelVisibility = !$rootScope.floatingpanelVisibility;
        var panel = angular.element( $('#floatingpanel') );
        var top = -50;
        if ($rootScope.floatingpanelVisibility)
            top = 0; 
        panel.css({'top': top });
    }

});

好的,它可以工作,但是...我想如果我继续这样,我的 app.run 很快就会超载,不是吗? 我也可以用我的“主包装器”上的控制器来完成,但它很愚蠢,它已经是我的应用程序范围了!

如何使用指令完成?
解决这个问题的“最佳方法”是什么?

你正在做的事情可以在不触及 DOM 的情况下完成,使用内置的 angular 指令本身,这里我只是使用 ng-show directive, if you want to put specific class to it then just use ng-class as well, and any animation can just be provided as css animations and with the inclusion of ngAnimate module it gets better you can use the classnames added to provide animation 行为。

示例:-

<div class="main-wrapper" ng-app="myapp">
   <div ng-controller="panelController as panel">
    <a href="#" class="trigger" ng-click="panel.toggle($event)">Click here to see the panel</a>
    <div  ng-show="panel.show" class='panel'> 
    <!-- <div  ng-class="{'show':panel.show}"> and define css rules for panel.show-->
        Hey ! I was hidden !
    </div>
   </div>
  </div>

在控制器中panelController

angular.module('app').controller('panelController', function($scope){
    var vm = this;
    vm.show = false;
    this.toggle  = function($event){
       $event.preventDefault();
       vm.show = !vm.show;
    }
});

我在这里只是使用 controller 作为语法,如果你愿意,你也可以使用 $scope,你甚至可以将其转换为指令。

根据您的问题更新进行编辑:

创建控制器并在需要时使用内置的 angular 指令比在确实不需要时使用 jquery/manual dom 操作没有什么不好.

<body>
  <div class="main-wrapper" ng-app="myapp" ng-controller="mainController as main">
    <div class="app">
      <div class="many-things">
        [...]
        <a href="#" class="trigger" ng-click="main.togglePanel($event)">Click here to see the panel</a>
        [...]
      </div>
    </div><!-- .app -->

    <div id="floatingpanel" ng-show="panel.show">
        Hey ! I was hidden !
    </div>

  </div> <!-- #mainwrapper -->
</body>

这可能是您遇到的错误答案,因为您似乎正在考虑不创建控制器或重新设计视图。

 .directive('togglePanel', function(jQuery){
      return {
         restrict: 'A',
         link:function(scope, elm, attr){
             elm.on('click', handleToggle);

             scope.$on('$destroy', function(){
                elm.off('click', handleToggle);
             });

             function handleToggle(e){
               var $target  = jQuery(attr.togglePanel);//Get the target
               //Do what you want to do with the target
             }
         }
      }

    }).constant('jQuery', window.jQuery);

并将其用作:

 <a href="#" class="trigger" toggle-panel="#floatingPanel">Click here to see the panel</a>

好的!感谢@PSL,我明白如果我在我的 ng-app 元素上设置一个控制器,这不是问题。

<div class="main-wrapper" ng-app="myapp" ng-controller="mainController as main">

我认为这是一种不好的做法...
有了 "mainController",我可以轻松地与我的 2 个元素交互:通过 ng-click 与 link 交互,通过 ng-class(或 ng-show 等与面板交互.) 我看不到更好的方法。如果我避免这个 "mainController" 我可以做一个指令但是触摸我的面板它迫使我访问范围之外的 DOM...

我必须改变主意,从 jquery 方式转为 angular 方式 (this link can help)。

我在 Angular 中看到的两种最常见的方法是通过根范围消息传递或通过服务。服务是两者的cleaner/better。

接受的答案假设一切都在同一个控制器下,这显然不是构建 OP 描述内容的方式。

想象一个由一堆小部件组成的页面 - 子视图或指令,全部由它们自己的 独立 控制器处理。这是处理 OP 在 Angular.

中描述为 "all that stuff" 的正确方法

现在,您需要一种方法来触发一个可以被任何其他事件响应的事件。如果你定义了一个服务,比如说,"pageEventService" 将函数公开给 initiate/trigger 事件 "togglePanel",以及 属性 保存状态 - "panelIsVisible" ,你可以在任何需要触发事件的地方引用该服务,并在面板中引用它来响应值。

.controller('SomeControllerThatCanTriggerEvent', function(pageEventService) {
    var model = this;
    model.togglePanel = pageEventService.togglePanel;
});

在此控制器控制的标记中的某处...

<a ng-click="model.togglePanel()">Toggle the Panel</a>

在面板控制器中...

.controller('PanelController', function(pageEventService) {
    var model = this;
    model.panelIsVisible = pageEventService.panelIsVisible;
});

并在面板标记中...

<div id="TheAlmightyPanel" ng-show="model.panelIsVisible">... cool stuff here ...</div>

这比 rootscope 消息传递更清晰,因为您可以明确说明 pageEventService 可以做什么以及谁引用它。您可以在视图、子视图和指令中使用它 - 无论您需要什么。