[angularjs] AngularJS 지시문에 정의 된 메소드를 호출하는 방법은 무엇입니까?

지시어가 있습니다. 코드는 다음과 같습니다.

.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {

            var center = new google.maps.LatLng(50.1, 14.4); 
            $scope.map_options = {
                zoom: 14,
                center: center,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            // create map
            var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
            var dirService= new google.maps.DirectionsService();
            var dirRenderer= new google.maps.DirectionsRenderer()

            var showDirections = function(dirResult, dirStatus) {
                if (dirStatus != google.maps.DirectionsStatus.OK) {
                    alert('Directions failed: ' + dirStatus);
                    return;
                  }
                  // Show directions
                dirRenderer.setMap(map);
                //$scope.dirRenderer.setPanel(Demo.dirContainer);
                dirRenderer.setDirections(dirResult);
            };

            // Watch
            var updateMap = function(){
                dirService.route($scope.dirRequest, showDirections); 
            };    
            $scope.$watch('dirRequest.origin', updateMap);

            google.maps.event.addListener(map, 'zoom_changed', function() {
                $scope.map_options.zoom = map.getZoom();
              });

            dirService.route($scope.dirRequest, showDirections);  
        }
    }
})

updateMap()사용자 조치 를 요청 하고 싶습니다 . 조치 단추가 지시문에 없습니다.

updateMap()컨트롤러에서 전화하는 가장 좋은 방법은 무엇입니까 ?



답변

분리 된 범위를 사용 =하려면 컨트롤러 범위에서 변수의 양방향 바인딩 을 사용하여 제어 개체를 전달할 수 있습니다 . 동일한 제어 오브젝트를 사용하여 페이지에서 동일한 지시문의 여러 인스턴스를 제어 할 수도 있습니다.

angular.module('directiveControlDemo', [])

.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {};
})

.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link: function(scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
  <div ng-controller="MainCtrl">
    <button ng-click="focusinControl.takeTablet()">Call directive function</button>
    <p>
      <b>In controller scope:</b>
      {{focusinControl}}
    </p>
    <p>
      <b>In directive scope:</b>
      <focusin control="focusinControl"></focusin>
    </p>
    <p>
      <b>Without control object:</b>
      <focusin></focusin>
    </p>
  </div>
</div>


답변

동작 버튼 $scope이 지시어 와 동일한 컨트롤러 를 사용한다고 가정하면 링크 기능 내부에 기능 updateMap을 정의 $scope하십시오. 그런 다음 컨트롤러는 작업 버튼을 클릭하면 해당 기능을 호출 할 수 있습니다.

<div ng-controller="MyCtrl">
    <map></map>
    <button ng-click="updateMap()">call updateMap()</button>
</div>
app.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {
            $scope.updateMap = function() {
                alert('inside updateMap()');
            }
        }
    }
});

fiddle


@FlorianF의 의견에 따라 지시문이 고립 된 범위를 사용하면 상황이 더 복잡합니다. 그것을 작동시키는 한 가지 방법이 있습니다 : 지시어 기능을 컨트롤러에 등록 할 지시어에 set-fn속성을 추가하십시오 map:

<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
    scope.updateMap = function() {
       alert('inside updateMap()');
    }
    scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
    $scope.setDirectiveFn = function(directiveFn) {
        $scope.directiveFn = directiveFn;
    };
}

fiddle


답변

의사 소통의 격리 된 범위에서 개체와의 통신을 용이하게하려는 유혹이있을 수 있지만, 이렇게하면 “스파게티”코드가 혼동 될 수 있습니다. 특히이 통신을 몇 가지 수준 (제어기, 명령, 중첩 지시문 등)

우리는 원래이 길을 갔지만, 더 많은 연구 결과에 따르면, 지시어가 서비스를 통한 통신에 사용하고 그 서비스의 속성에 $ watch를 사용하여 이벤트와 속성을 노출하는 것이 더 이해하기 쉽고 코드를 유지하고 읽기 쉽다는 것을 발견했습니다. 의사 소통을 위해 이러한 변경 사항에 대응해야하는 지침 또는 기타 통제.

이 추상화는 AngularJS의 의존성 주입 프레임 워크와 매우 잘 작동하므로 해당 이벤트에 반응 해야하는 모든 항목에 서비스를 주입 할 수 있습니다. Angular.js 파일을 보면이 방식의 지시문도 이러한 방식으로 서비스와 $ watch를 사용한다는 것을 알 수 있으며 격리 된 범위에서 이벤트를 노출하지 않습니다.

마지막으로, 서로 종속 된 지시문간에 통신해야하는 경우, 통신 수단으로 해당 지시문간에 컨트롤러를 공유하는 것이 좋습니다.

AngularJS의 Wiki for Best Practices 도 다음과 같이 언급합니다.

원자 적 이벤트에는. $ broadcast (),. $ emit () 및. $ on () 만 사용하십시오. 전체 앱에서 전 세계적으로 관련된 이벤트 (예 : 사용자 인증 또는 앱 종료). 모듈, 서비스 또는 위젯 관련 이벤트를 원하는 경우 서비스, 지시문 컨트롤러 또는 타사 라이브러리를 고려해야합니다.

  • $ scope. $ watch ()는 이벤트의 필요성을 대체해야합니다
  • 서비스를 직접 주입하고 메소드를 호출하는 것도 직접적인 의사 소통에 유용합니다
  • 지시문은 지시문 컨트롤러를 통해 서로 직접 통신 할 수 있습니다.

답변

Oliver의 대답을 바탕으로-지시문의 내부 메소드에 항상 액세스 할 필요는 없으며, 이러한 경우 control오류가 발생하지 않도록 빈 객체를 작성 하고 지시문에 attr을 추가하지 않아도됩니다 ( cannot set property 'takeTablet' of undefined).

지시문 내의 다른 위치에서이 방법을 사용할 수도 있습니다.

scope.control존재 하는지 확인 하고 공개 모듈 패턴과 유사한 방식으로 메소드를 설정합니다.

app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{control}}</div>',
    scope: {
      control: '='
    },
    link : function (scope, element, attrs) {
      var takenTablets = 0;
      var takeTablet = function() {
        takenTablets += 1;
      }

      if (scope.control) {
        scope.control = {
          takeTablet: takeTablet
        };
      }
    }
  };
});


답변

솔직히 말해서, 나는이 스레드에서 어떤 대답도 확신하지 못했습니다. 그래서, 여기 내 해결책이 있습니다 :

지시문 처리기 (관리자) 접근

이 방법은 지시어 $scope가 공유 된 것인지 분리 된 것인지 아닌지에 관계없이

factory지시문 인스턴스를 등록하는 A

angular.module('myModule').factory('MyDirectiveHandler', function() {
    var instance_map = {};
    var service = {
        registerDirective: registerDirective,
        getDirective: getDirective,
        deregisterDirective: deregisterDirective
    };

    return service;

    function registerDirective(name, ctrl) {
        instance_map[name] = ctrl;
    }

    function getDirective(name) {
        return instance_map[name];
    }

    function deregisterDirective(name) {
        instance_map[name] = null;
    }
});

지시문 코드는 일반적으로 지시문 컨트롤러 내부에 DOM을 다루지 않는 모든 논리를 넣습니다. 그리고 핸들러 내부에 컨트롤러 인스턴스를 등록

angular.module('myModule').directive('myDirective', function(MyDirectiveHandler) {
    var directive = {
        link: link,
        controller: controller
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        var name = $attrs.name;

        this.updateMap = function() {
            //some code
        };

        MyDirectiveHandler.registerDirective(name, this);

        $scope.$on('destroy', function() {
            MyDirectiveHandler.deregisterDirective(name);
        });
    }
})

템플릿 코드

<div my-directive name="foo"></div>

factory공개적으로 노출 된 메소드를 실행하고 실행 하여 컨트롤러 인스턴스에 액세스

angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) {
    $scope.someFn = function() {
        MyDirectiveHandler.get('foo').updateMap();
    };
});

각도의 접근

그들이 다루는 방법에 대한 앵귤러의 책에서 잎을 가져 오기

<form name="my_form"></form>

$ parse를 사용 하고 $parent범위에서 컨트롤러를 등록하십시오 . 이 기법은 분리 된 $scope지시문 에서는 작동하지 않습니다 .

angular.module('myModule').directive('myDirective', function($parse) {
    var directive = {
        link: link,
        controller: controller,
        scope: true
    };

    return directive;

    function link() {
        //link fn code
    }

    function controller($scope, $attrs) {
        $parse($attrs.name).assign($scope.$parent, this);

        this.updateMap = function() {
            //some code
        };
    }
})

다음을 사용하여 컨트롤러 내부에 액세스하십시오. $scope.foo

angular.module('myModule').controller('MyController', function($scope) {
    $scope.someFn = function() {
        $scope.foo.updateMap();
    };
});


답변

조금 늦었지만 이것은 지시문에서 함수를 호출하는 격리 된 범위와 “이벤트”가있는 솔루션입니다. 이 솔루션은 영감 이 SO 게시물 에 의해 satchmorun 과 모듈과 API를 추가합니다.

//Create module
var MapModule = angular.module('MapModule', []);

//Load dependency dynamically
angular.module('app').requires.push('MapModule');

지시문과 통신 할 API를 작성하십시오. addUpdateEvent는 이벤트를 이벤트 배열에 추가하고 updateMap은 모든 이벤트 함수를 호출합니다.

MapModule.factory('MapApi', function () {
    return {
        events: [],

        addUpdateEvent: function (func) {
            this.events.push(func);
        },

        updateMap: function () {
            this.events.forEach(function (func) {
                func.call();
            });
        }
    }
});

이벤트를 제거하려면 기능을 추가해야 할 수도 있습니다.

지시문에서 MapAPI에 대한 참조를 설정하고 MapApi.updateMap이 호출 될 때 $ scope.updateMap을 이벤트로 추가하십시오.

app.directive('map', function () {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: '....',
        controller: function ($scope, $http, $attrs, MapApi) {

            $scope.api = MapApi;

            $scope.updateMap = function () {
                //Update the map 
            };

            //Add event
            $scope.api.addUpdateEvent($scope.updateMap);

        }
    }
});

“main”컨트롤러에서 MapApi에 대한 참조를 추가하고 MapApi.updateMap ()을 호출하여 맵을 업데이트하십시오.

app.controller('mainController', function ($scope, MapApi) {

    $scope.updateMapButtonClick = function() {
        MapApi.updateMap();
    };
}


답변

지시문이 상위 범위에서 함수를 정의하도록 허용하는 데 사용할 수있는 DOM 속성을 지정할 수 있습니다. 그러면 상위 범위는 다른 방법과 같이이 메소드를 호출 할 수 있습니다. 여기 plunker은. 아래는 관련 코드입니다.

clearfn 부모 범위가 범위 속성을 전달하여 지시문이 원하는 동작을 수행하는 함수로 설정할 수있는 지시문 요소의 속성입니다.

<!DOCTYPE html>
<html ng-app="myapp">
  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <style>
      my-box{
        display:block;
        border:solid 1px #aaa;
        min-width:50px;
        min-height:50px;
        padding:.5em;
        margin:1em;
        outline:0px;
        box-shadow:inset 0px 0px .4em #aaa;
      }
    </style>
  </head>
  <body ng-controller="mycontroller">
    <h1>Call method on directive</h1>
    <button ng-click="clear()">Clear</button>
    <my-box clearfn="clear" contentEditable=true></my-box>
    <script>
      var app = angular.module('myapp', []);
      app.controller('mycontroller', function($scope){
      });
      app.directive('myBox', function(){
        return {
          restrict: 'E',
          scope: {
            clearFn: '=clearfn'
          },
          template: '',
          link: function(scope, element, attrs){
            element.html('Hello World!');
            scope.clearFn = function(){
              element.html('');
            };
          }
        }
      });
    </script>
  </body>
</html>