简体   繁体   English

AngularJs:当存在“全局”时更改 DOM 元素的最佳方法是什么?

[英]AngularJs : what is the best way to changing DOM elements when there are “global”?

I'm from JQuery, and my first steps with AngularJs are getting me a little bit confused...我来自 JQuery,我使用 AngularJs 的第一步让我有点困惑......

My case :我的情况 :
I have a panel (#floatingpanel) that is hidden with css (top:-50px), out of the viewport.我有一个面板(#floatingpanel),它用 css(顶部:-50px)隐藏在视口之外。
I have a link (a.trigger), in the deepest of my app, and when you click on that link the "top" property of my panel goes from "-50px" to "0px" to make it visible (you click to toggle the visibility).我在我的应用程序的最深处有一个链接 (a.trigger),当您单击该链接时,面板的“顶部”属性会从“-50px”变为“0px”以使其可见(您单击切换可见性)。 The panel is just at this end of the main container of my page, but the link it's in hundreds of others div .面板就在我页面主容器的这一端,但它的链接在数百个其他div

Let's have a look to the DOM structure :让我们来看看 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>

In jQuery, It would be :在 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);
});

How can I make it with AngularJs ?我怎样才能用 AngularJs 做到这一点?

I would do it like that :我会这样做:

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 });
    }

});

Ok, it works but... i guess my app.run will be overloaded very soon if I continue this way, no ?好的,它有效,但是...我想如果我继续这种方式,我的app.run很快就会超载,不是吗? I also can do it with a controller on my "main-wrapper" but it's stupid, it's already my app scope !我也可以用我的“主包装器”上的控制器来做到这一点,但这很愚蠢,它已经是我的应用程序范围了!

How can I do it with a directive ?我怎样才能用指令做到这一点?
What could be the "best way" to solve that mess ?解决这个烂摊子的“最佳方法”是什么?

The two most common ways I've seen to do this in Angular is via rootscope messaging or via a service.我在 Angular 中看到的两种最常见的方法是通过 rootscope 消息传递或通过服务。 The service is the cleaner/better of the two.该服务是两者中更清洁/更好的。

The accepted answer assumes everything is under the same controller, and that's expressly not the way to structure what the OP describes.接受的答案假设一切都在同一个控制器下,这显然不是构建 OP 所描述内容的方式。

Imagine a page comprised of a bunch of widgets - either child views or directives, all handled by their own, independent controllers.想象一个页面由一堆小部件组成——无论是子视图还是指令,都由它们自己的独立控制器处理。 That's the correct way to handle what the OP describes as "all that stuff" in Angular.这是处理 OP 在 Angular 中描述为“所有这些东西”的正确方法。

Now, you need a way to trigger an event in one that can be responded to by any of the others.现在,您需要一种在一个事件中触发事件的方法,该事件可由任何其他事件响应。 If you define a service, say, "pageEventService" that exposes a function to initiate/trigger the event "togglePanel", as well as a property that holds the state - "panelIsVisible", you can reference that service wherever you need to trigger the event, and reference it in the Panel to respond to the value.如果您定义了一个服务,比如“pageEventService”,它公开一个函数来启动/触发事件“togglePanel”,以及一个保存状态的属性 - “panelIsVisible”,您可以在需要触发的任何地方引用该服务事件,并在面板中引用它以响应该值。

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

somewhere in the markup controlled by this controller...在此控制器控制的标记中的某处...

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

in the Panel controller...在面板控制器中...

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

and in the Panel markup...并在面板标记中...

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

This is cleaner than rootscope messaging because you can be explicit about what pageEventService can do and about who references it.这比 rootscope 消息传递更清晰,因为您可以明确说明 pageEventService 可以做什么以及谁引用它。 You can use it in views, child-views, and directives - wherever you need it.你可以在视图、子视图和指令中使用它 - 无论你需要它。

What you are doing can just be done without touching DOM, using built in angular directives itself, here i am just using 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 behavior.你正在做的事情可以在不接触 DOM 的情况下完成,使用内置的 angular 指令本身,这里我只是使用ng-show指令,如果你想把特定的类放到它然后只使用ng-class ,以及任何动画可以仅作为 css 动画提供,并且通过包含ngAnimate模块它变得更好,您可以使用添加的类名来提供 动画行为。

Example:-例子:-

<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>

in the controller panelController在控制器panelController控制器中

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

I am here just using controller As syntax you could use $scope as well if you would like and you can even convert this to a directive.我在这里只是使用控制器作为语法,如果您愿意,您也可以使用 $scope ,您甚至可以将其转换为指令。

Edit based on your question update:根据您的问题更新进行编辑:

There is nothing bad in creating a controller and using built in angular directives as and when you need then rather than using jquery/manual dom manipulation when you really do not need to.创建控制器并在需要时使用内置的 angular 指令没有什么不好,而不是在您确实不需要时使用 jquery/手动 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>

And here is the bad answer probably you are onto since you seem to be looking at not creating a controller or redesign your views.这可能是您遇到的错误答案,因为您似乎不打算创建控制器或重新设计视图。

 .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);

and use it as:并将其用作:

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

Ok !好的 ! Thanks to @PSL, I've understood that it's not a problem if I setup a controller on my ng-app element.感谢@PSL,我明白如果我在我的 ng-app 元素上设置一个控制器,这不是问题。

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

I thought it was a bad practice...我认为这是一个不好的做法......
With that "mainController" I can easily interact with my 2 elements : with the link via an ng-click , and with the panel via an ng-class (or ng-show etc.) I cannot see a better way to do.有了这个“mainController”,我可以轻松地与我的 2 个元素进行交互:通过ng-click与链接进行交互,通过ng-class (或ng-show等)与面板进行交互。我看不到更好的方法。 If I avoid this "mainController" I could make a directive but to touch my panel it forces me to access DOM outside of the scope...如果我避免使用这个“mainController”,我可以制定一个指令,但要触摸我的面板,它会迫使我访问范围之外的 DOM...

I definitely have to change my mind from the jquery way to the angular way ( this link can help ).我绝对必须将我的想法从 jquery 方式转变为 angular 方式( 此链接可以提供帮助)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM