programing

AngularJS : 디렉티브에서 글로벌이벤트에 바인드 하는 최선의 방법

showcode 2023. 3. 21. 22:44
반응형

AngularJS : 디렉티브에서 글로벌이벤트에 바인드 하는 최선의 방법

앵귤러에서의 상황을 상상하다글로벌 이벤트에 응답해야 하는 지시문을 생성하는 JS.이 경우, 예를 들어 창 크기 조정 이벤트가 발생한다고 가정합니다.

이를 위한 최선의 접근법은 무엇입니까?제가 보기엔 두 가지 방법이 있습니다. 1가지입니다.모든 디렉티브가 이벤트에 바인딩되어 현재 요소 2에서 마법처럼 수행되도록 합니다.DOM 셀렉터를 실행하는 글로벌이벤트 리스너를 생성하여 로직을 적용할 각 요소를 가져옵니다.

옵션 1은 일부 작업을 수행할 요소에 이미 액세스할 수 있다는 장점이 있습니다.단, 옵션2는 같은 이벤트에서 (각 디렉티브에 대해) 여러 번 바인드할 필요가 없다는 장점이 있어 퍼포먼스의 이점이 있습니다.

두 가지 옵션을 모두 나타냅니다.

옵션 1:

angular.module('app').directive('myDirective', function(){

     function doSomethingFancy(el){
         // In here we have our operations on the element
    }

    return {
        link: function(scope, element){
             // Bind to the window resize event for each directive instance.
             angular.element(window).on('resize', function(){
                  doSomethingFancy(element);
             });
        }
    };
});

옵션 2:

angular.module('app').directive('myDirective', function(){

    function doSomethingFancy(){
         var elements = document.querySelectorAll('[my-directive]');
         angular.forEach(elements, function(el){
             // In here we have our operations on the element
         });
    }

    return {
        link: function(scope, element){
             // Maybe we have to do something in here, maybe not.
        }
    };

    // Bind to the window resize event only once.
    angular.element(window).on('resize', doSomethingFancy);
});

두 가지 접근법 모두 잘 작동하고 있지만, 나는 두 번째 옵션이 실제로는 '각선적인' 것이 아니라고 느낀다.

좋은 생각 있어요?

윈도우 크기 조정과 같은 글로벌 이벤트를 효과적으로 현지화하기 위해 다른 방법을 선택했습니다.다른 지시어를 통해 Javascript 이벤트를 Angular scope 이벤트로 변환합니다.

app.directive('resize', function($window) {
  return {
    link: function(scope) {
      function onResize(e) {
        // Namespacing events with name of directive + event to avoid collisions
        scope.$broadcast('resize::resize');
      }

      function cleanUp() {
        angular.element($window).off('resize', onResize);
      }

      angular.element($window).on('resize', onResize);
      scope.$on('$destroy', cleanUp);
    }
  }
});

기본적인 경우 앱의 루트 요소에서 사용할 수 있습니다.

<body ng-app="myApp" resize>...

그런 다음 다른 방향에서 이벤트를 듣습니다.

<div my-directive>....

다음과 같이 코드화:

app.directive('myDirective', function() {
  return {
    link: function(scope, element) {
      scope.$on('resize::resize', function() {
        doSomethingFancy(element);
      });
    });
  }
});

이는 다른 접근법에 비해 많은 이점이 있습니다.

  • 지시문이 어떻게 사용되는지에 대한 정확한 형태에 취약하지 않다.에는 '2'가 필요합니다.my-directive가 다음 :my:directive,data-my-directive,x-my-directive,my_directive안내서에서 알 수 있듯이

  • Javascript 이벤트가 Angular 이벤트로 변환되는 방법에 영향을 주는 단일 장소가 있습니다.에 javascript를 합시다.resize이벤트, Lodash 디바운스 기능을 사용합니다.수정이 가능합니다.resize★★★★

    angular.element($window).on('resize', $window._.debounce(function() {
      scope.$broadcast('resize::resize');
    },500));
    
  • 발생은 아니기 $rootScope , , , , , , , , , , you you you you you you you you you you you you you you you you you you you you you you you you 로 이동하는 것만으로 이벤트를 앱의 일부만 제한할 수 resize

    <body ng-app="myApp">
      <div>
        <!-- No 'resize' events here -->
      </div>
      <div resize>
        <!-- 'resize' events are $broadcast here -->
      </div>
    
  • 옵션을 사용하여 지시문을 확장하고 앱의 다른 부분에서 다르게 사용할 수 있습니다.다른 부분에서 다른 디버깅된 버전을 원한다고 가정해 보십시오.

    link: function(scope, element, attrs) {
      var wait = 0;
      attrs.$observe('resize', function(newWait) {
        wait = $window.parseInt(newWait || 0);
      });
      angular.element($window).on('resize', $window._.debounce(function() {
        scope.$broadcast('resize::resize');
      }, wait));
    }
    

    사용처:

    <div resize>
      <!-- Undebounced 'resize' Angular events here -->
    </div>
    <div resize="500">
      <!-- 'resize' is debounced by 500 milliseconds -->
    </div>
    
  • 나중에 도움이 될 수 있는 다른 이벤트와 함께 디렉티브를 확장할 수 있습니다. ★★★★★★★★★★★★★★★★★★★★★★★★★★resize::heightIncreaseresize::heightDecrease,resize::widthIncrease,resize::widthDecrease그런 다음 앱에서 창의 정확한 크기를 기억하고 처리하는 작업을 한 곳에서 수행할 수 있습니다.

  • 이벤트와 함께 데이터를 전달할 수 있습니다.뷰포트의 높이/폭과 같이, 크로스 브라우저의 문제에 대처할 필요가 있는 경우(IE의 서포트가 필요한 기간이나, 도움이 되는 다른 라이브러리가 포함되어 있는지에 따라 다릅니다).

    angular.element($window).on('resize', function() {
      // From http://stackoverflow.com/a/11744120/1319998
      var w = $window,
          d = $document[0],
          e = d.documentElement,
          g = d.getElementsByTagName('body')[0],
          x = w.innerWidth || e.clientWidth || g.clientWidth,
          y = w.innerHeight|| e.clientHeight|| g.clientHeight;
      scope.$broadcast('resize::resize', {
        innerWidth: x,
        innerHeight: y
      });
    });
    

    나중에 데이터에 추가할 수 있는 단일 위치를 제공합니다.예를 들어, 마지막으로 디버깅된 이벤트 이후의 치수 차이를 전송하려고 합니까?이전 크기를 기억하고 차액을 보내려면 약간의 코드를 추가할 수 있습니다.

기본적으로 이 설계는 글로벌 Javascript 이벤트를 로컬 Angular 이벤트로, 로컬 앱뿐만 아니라 로컬 앱의 다른 부분으로 구성 가능한 방식으로 변환하는 방법을 제공합니다.

프레임워크 위에서 개발할 때 관용어를 설계하기 전에 문제에 대해 불가지론적으로 생각하는 것이 도움이 되는 경우가 많습니다."무엇"과 "왜"에 대답하면 "방법"이 없어집니다.

.doSomethingFancy()이 디렉티브의 인스턴스와 관련된 데이터, 기능 세트 또는 도메인 객체가 있습니까?문제입니까? 예를 들어, '조율'을 하는 것 입니까?width ★★★★★★★★★★★★★★★★★」height할 때 작업에 적합한 도구를 사용하고 있는지 확인하십시오. 작업에 핀셋이 필요하고 독립형 한 켤레에 접근할 수 있을 때 스위스 군용 나이프를 통째로 가져오지 마십시오.는 '이러다'라는 doSomethingFancy()는 순전히 프레젠테이션 함수입니다.

Angular 이벤트에서 글로벌 브라우저 이벤트를 래핑하는 문제는 다음과 같은 간단한 실행 단계 구성으로 해결할 수 있습니다.

angular.module('myApp')
    .run(function ($rootScope) {
        angular.element(window).on('resize', function () {
            $rootScope.$broadcast('global:resize');  
        })
    })
;

Angular는 각각의 Angular에 $digest하지만 동일한 기능을 사용할 수 있습니다.

사항은 '동작'입니다.n의 모든 이 있습니다다시 한 번 말씀드리지만, 모든 지시사항이 필요하지 않다면, 이것을 실현하는 다른 방법이 있습니다.위의 런 블록에서 접근 방식을 확장하거나 조정할 수 있습니다.

angular.module('myApp')
    .run(function () {
        angular.element(window).on('resize', function () {
            var elements = document.querySelectorAll('.reacts-to-resize');
        })
    })
;

크기 조정 이벤트에 대해 발생해야 하는 보다 복잡한 로직이 있는 경우에도 하나 이상의 디렉티브가 이 로직을 처리하는 가장 좋은 방법이라고는 할 수 없습니다.앞에서 설명한 익명 실행 단계 설정 대신 인스턴스화된 단순 중개 서비스를 사용할 수 있습니다.

/**
 * you can inject any services you want: $rootScope if you still want to $broadcast (in)
 * which case, you'd have a "Publisher" instead of a "Mediator"), one or more services 
 * that maintain some domain objects that you want to manipulate, etc.
 */
function ResizeMediator($window) {
    function doSomethingFancy() {
        // whatever fancy stuff you want to do
    }

    angular.element($window).bind('resize', function () {
        // call doSomethingFancy() or maybe some other stuff
    });
}

angular.module('myApp')
    .service('resizeMediator', ResizeMediator)
    .run(resizeMediator)
;

이제 유닛 테스트도 가능하지만 사용되지 않는 실행 단계는 실행하지 않는 캡슐화된 서비스가 제공되었습니다.

몇 가지 우려 사항도 이 결정에 반영될 것입니다.

  • 데드 리스너 - 옵션 1을 사용하면 지시어의 각 인스턴스에 대해 이벤트 리스너를 하나 이상 작성합니다.이러한 요소가 DOM 에 동적으로 추가 또는 삭제되고 있을 때, 콜을 하지 않는 경우.$on('$destroy')이벤트 핸들러의 요소가 존재하지 않게 되었을 때 이벤트 핸들러가 스스로 적용될 위험이 있습니다.
  • 폭/높이 연산자의 성능 - 글로벌 이벤트가 브라우저 크기 조정이라는 점을 고려할 때 박스 모델 로직이 있다고 가정합니다.그렇지 않은 경우 이를 무시하십시오. 그렇지 않은 경우 브라우저 리플로우는 성능 저하의 큰 원인이 될 수 있으므로 액세스 중인 속성과 빈도에 주의해야 합니다.

이 답변은 당신이 기대했던 것처럼 "각도"가 아닐 수도 있지만, 박스 모델만의 논리를 가정하여 제가 이해하는 방식으로 문제를 해결합니다.

제 생각에는 $window 서비스를 사용하는 방법 #1과 약간의 수정으로 하겠습니다.

angular.module('app').directive('myDirective', function($window){

     function doSomethingFancy(el){
         // In here we have our operations on the element
    }

    return {
        link: function(scope, element){
             // Bind to the window resize event for each directive instance.
             anguar.element($window).bind('resize', function(){
                  doSomethingFancy(element);
             });
        }
    };
});

#2 이 접근법과 약간의 사고방식의 변화 - 이 이벤트 청취자를 say app.run의 높은 위치에 배치할 수 있습니다.그리고 이벤트 발생 시 디렉티브가 픽업하여 이벤트 발생 시 화려한 동작을 하는 다른 이벤트를 방송할 수 있습니다.

편집: 이 방법은 생각할수록 첫 번째 방법보다 더 좋아지기 시작합니다.윈도우 크기 조정 이벤트를 청취하는 매우 견고한 방법 - 향후 다른 누군가가 이 정보를 "알아야 할" 필요가 있을 수 있습니다.또, 이러한 조작을 실시하지 않는 한, window.size 이벤트의 다른 이벤트청취자를 설정할 필요가 있습니다.

app.run

app.run(function($window, $rootScope) {
  angular.element($window).bind('resize', function(){
     $rootScope.$broadcast('window-resize');
  });
}

지시 angular.module('app').디렉티브('myDirective', 함수($rootScope){

     function doSomethingFancy(el){
         // In here we have our operations on the element
    }

    return {
        link: function(scope, element){
             // Bind to the window resize event for each directive instance.
             $rootScope.$on('window-resize', function(){
                  doSomethingFancy(element);
             });
        }
    };
});

마지막으로 ui-bootstrap과 같은 angular-ui를 따라가는 것이 작업 방법의 훌륭한 원천입니다.저는 이 사람들로부터 많은 것을 배웠습니다. 예를 들어, 각도 테스트를 배우는 즐거움 같은 것들이죠.체크 아웃에 매우 깨끗한 코드 베이스를 제공합니다.

은 Angular가 (Angular)를 수 있는 많은 하므로 더.my-directive,my_directive,my:directive,x-my-directive,data-my-directive(등)을 모두 커버하는 CSS 셀렉터는 매우 복잡할 수 있습니다.

명령어를 내부적으로만 사용하거나 하나의 단어로 구성되는 경우, 이는 큰 문제가 되지 않을 수 있습니다.그러나 코딩 규칙이 다른 다른 개발자가 사용자의 지시사항을 사용할 수 있는 경우에는 두 번째 방법을 사용하지 않는 것이 좋습니다.

하지만 난 실용적일 거야소수의 인스턴스를 다루고 있다면 #1로 넘어갑니다.수백 개가 있으면 2번으로 할게요.

여기 한 가지 방법이 있습니다. 요소를 배열에 저장하기만 하면 "글로벌 이벤트"에서는 요소를 루프하여 필요한 작업을 수행할 수 있습니다.

angular.module('app').directive('myDirective', function($window){

    var elements = [];

    $window.on('resize', function(){
       elements.forEach(function(element){
           // In here we have our operations on the element
       });
    });

    return {
        link: function(scope, element){
            elements.push(element);
        }
    };
});

언급URL : https://stackoverflow.com/questions/23272169/angularjs-what-is-the-best-way-to-bind-to-a-global-event-in-a-directive

반응형