[angularjs] AngularJS의 * 자신의 범위 *로 사용자 지정 지시문 내에서 부모 범위에 액세스하는 방법은 무엇입니까?

지시문 내에서 “부모”범위에 액세스하는 방법을 찾고 있습니다. 범위, transclude, require, 위의 변수 (또는 범위 자체) 전달 등의 모든 조합. 나는 완전히 뒤로 기꺼이 구부리고 싶지만 완전히 해킹하거나 유지할 수없는 것을 피하고 싶습니다. 예를 들어, $scopepreLink 매개 변수에서 가져 와서 $sibling개념 “부모”를 찾기 위해 범위를 반복 함으로써 지금 당장 할 수 있다는 것을 알고 있습니다.

내가 정말로 원하는 것은 $watch부모 범위에서 식 을 사용할 수 있도록 하는 것입니다. 그렇게 할 수 있다면 여기서 시도하고있는 것을 성취 할 수 있습니다 :
AngularJS-변수로 부분을 렌더링하는 방법?

중요한 사항 은 지시문을 동일한 상위 범위 내에서 재사용 할 수 있어야한다는 것입니다. 따라서 기본 동작 (scope : false)이 작동하지 않습니다. 지시문의 인스턴스마다 개별 범위가 필요 $watch하며 부모 범위에있는 변수가 필요합니다 .

코드 샘플의 가치는 1000 단어이므로

app.directive('watchingMyParentScope', function() {
    return {
        require: /* ? */,
        scope: /* ? */,
        transclude: /* ? */,
        controller: /* ? */,
        compile: function(el,attr,trans) {
            // Can I get the $parent from the transclusion function somehow?
            return {
                pre: function($s, $e, $a, parentControl) {
                    // Can I get the $parent from the parent controller?
                    // By setting this.$scope = $scope from within that controller?

                    // Can I get the $parent from the current $scope?

                    // Can I pass the $parent scope in as an attribute and define
                    // it as part of this directive's scope definition?

                    // What don't I understand about how directives work and
                    // how their scope is related to their parent?
                },
                post: function($s, $e, $a, parentControl) {
                    // Has my situation improved by the time the postLink is called?
                }
            }
        }
    };
});



답변

AngularJS에서 스코프 프로토 타입 / 프로토 타입 상속의 뉘앙스는 무엇입니까?를 참조하십시오 .

요약 : 지시문이 부모 ( $parent) 범위에 액세스하는 방법 은 지시문이 생성하는 범위 유형에 따라 다릅니다.

  1. default ( scope: false)-지시문이 새 범위를 작성하지 않으므로 여기에는 상속이 없습니다. 지시문의 범위는 부모 / 컨테이너와 동일한 범위입니다. 링크 함수에서 첫 번째 매개 변수 (일반적으로 scope)를 사용하십시오 .

  2. scope: true-지시문은 부모 범위에서 프로토 타입으로 상속되는 새로운 자식 범위를 만듭니다. 부모 범위에 정의 된 속성 scope은 프로토 타입 상속으로 인해 지시문에서 사용할 수 있습니다 . 프리미티브 스코프 속성에 쓰는 것을주의하십시오. 지시문 범위에 새 속성이 만들어집니다 (같은 이름의 부모 범위 속성을 숨기거나 가리기).

  3. scope: { ... }-지시문은 새로운 분리 / 분리 범위를 만듭니다. 부모 범위를 프로토 타입으로 상속하지 않습니다. 을 사용하여 부모 범위에 계속 액세스 할 수 $parent있지만 일반적으로 권장되지는 않습니다. 대신 지정해야하는 부모 범위 속성을 (및 / 또는 기능) 추가 지시어 사용되는 동일한 요소에 속성의 사용을 통해 지침의 요구 =, @그리고 &표기법을.

  4. transclude: true-지시문은 부모 범위에서 프로토 타입으로 상속되는 새로운 “번역 된”하위 범위를 작성합니다. 지시문이 분리 범위를 작성하는 경우 변환 된 범위와 분리 범위는 형제입니다. $parent각 범위 의 속성은 동일한 부모 범위를 참조합니다.
    Angular v1.3 업데이트 : 지시어가 격리 범위를 생성하는 경우 이제 포함 된 범위는 격리 범위의 자식입니다. 포함 및 격리 범위는 더 이상 형제가 아닙니다. $parent포함 된 범위 의 속성은 이제 격리 범위를 참조합니다.

위의 링크에는 4 가지 유형의 예와 그림이 있습니다.

지시어의 컴파일 함수에서 범위에 액세스 할 수 없습니다 ( https://github.com/angular/angular.js/wiki/Understanding-Directives ). 링크 함수에서 지시문의 범위에 액세스 할 수 있습니다.

보고있다:

위의 1과 2의 경우 : 일반적으로 속성을 통해 지시문에 필요한 부모 속성을 지정한 다음 $ watch합니다.

<div my-dir attr1="prop1"></div>

scope.$watch(attrs.attr1, function() { ... });

객체 속성을보고 있다면 $ parse를 사용해야합니다.

<div my-dir attr2="obj.prop2"></div>

var model = $parse(attrs.attr2);
scope.$watch(model, function() { ... });

위의 3. (범위 분리)의 경우 @또는 =표기법을 사용하여 지시문 속성에 지정한 이름을 확인하십시오 .

<div my-dir attr3="{{prop3}}" attr4="obj.prop4"></div>

scope: {
  localName3: '@attr3',
  attr4:      '='  // here, using the same name as the attribute
},
link: function(scope, element, attrs) {
   scope.$watch('localName3', function() { ... });
   scope.$watch('attr4',      function() { ... });


답변

컨트롤러 메소드에 액세스하는 것은 지시문 컨트롤러 / 링크 / 범위에서 상위 범위의 메소드에 액세스하는 것을 의미합니다.

지시어가 부모 범위를 공유 / 상속하는 경우 부모 범위 메서드를 호출하는 것이 매우 간단합니다.

Isolated 지시문 범위에서 부모 범위 메서드에 액세스하려는 경우 더 많은 작업이 필요합니다.

분리 된 지시문 범위에서 부모 범위 메서드를 호출하거나 부모 범위 변수 ( 특히 옵션 # 6 )를 볼 수있는 옵션은 거의 없습니다 (아래에 나열된 것보다 많을 수 있음 ).

참고 내가 사용하는 것이 link function이러한 예에 있지만, 당신이 사용할 수있는 directive controller요구 사항에뿐만 아니라 기반으로합니다.

옵션 1. 객체 리터럴과 지시문 HTML 템플릿을 통해

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChanged({selectedItems:selectedItems})" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]

});

작업 plnkr : http://plnkr.co/edit/rgKUsYGDo9O3tewL6xgr?p=preview

옵션 # 2. 객체 리터럴과 지시문 링크 / 범위를 통해

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html",
    link: function (scope, element, attrs){
      scope.selectedItemsChangedDir = function(){
        scope.selectedItemsChanged({selectedItems:scope.selectedItems});  
      }
    }
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

작업 plnkr : http://plnkr.co/edit/BRvYm2SpSpBK9uxNIcTa?p=preview

옵션 # 3. 함수 참조 및 지시문 HTML 템플리트를 통해

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-change="selectedItemsChanged()(selectedItems)" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems:'=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

작업 plnkr : http://plnkr.co/edit/Jo6FcYfVXCCg3vH42BIz?p=preview

옵션 # 4. 함수 참조 및 지시문 링크 / 범위를 통해

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=',
      selectedItemsChanged: '&'
    },
    templateUrl: "itemfilterTemplate.html",
    link: function (scope, element, attrs){
      scope.selectedItemsChangedDir = function(){
        scope.selectedItemsChanged()(scope.selectedItems);  
      }
    }
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.selectedItemsChanged = function(selectedItems1) {
    $scope.selectedItemsReturnedFromDirective = selectedItems1;
  }

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]

});

작업 plnkr : http://plnkr.co/edit/BSqx2J1yCY86IJwAnQF1?p=preview

옵션 # 5 : ng-model 및 양방향 바인딩을 통해 상위 범위 변수를 업데이트 할 수 있습니다. . 따라서 경우에 따라 상위 범위 함수를 호출하지 않아도됩니다.

index.html

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>

    <p> Directive Content</p>
    <sd-items-filter ng-model="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>


    <P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}} </p>

  </body>

</html>

itemfilterTemplate.html

<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>

app.js

var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      items: '=',
      selectedItems: '=ngModel'
    },
    templateUrl: "itemfilterTemplate.html"
  }
})

app.controller('MainCtrl', function($scope) {
  $scope.name = 'TARS';

  $scope.selectedItems = ["allItems"];

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
    }]
});

작업 plnkr : http://plnkr.co/edit/hNui3xgzdTnfcdzljihY?p=preview

옵션 # 6 : 통과 $watch$watchCollectionitems 위의 모든 예에서
양방향 바인딩입니다 . 항목이 상위 범위에서 수정되면 지시문의 항목도 변경 사항을 반영합니다.

부모 범위에서 다른 속성이나 객체를보고 싶다면 아래에서 $watch$watchCollection같이 사용하십시오.

html

<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <p>Hello {{user}}!</p>
  <p>directive is watching name and current item</p>
  <table>
    <tr>
      <td>Id:</td>
      <td>
        <input type="text" ng-model="id" />
      </td>
    </tr>
    <tr>
      <td>Name:</td>
      <td>
        <input type="text" ng-model="name" />
      </td>
    </tr>
    <tr>
      <td>Model:</td>
      <td>
        <input type="text" ng-model="model" />
      </td>
    </tr>
  </table>

  <button style="margin-left:50px" type="buttun" ng-click="addItem()">Add Item</button>

  <p>Directive Contents</p>
  <sd-items-filter ng-model="selectedItems" current-item="currentItem" name="{{name}}" selected-items-changed="selectedItemsChanged" items="items"></sd-items-filter>

  <P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}}</p>
</body>

</html>

스크립트 app.js

var app = angular.module ( ‘plunker’, []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      name: '@',
      currentItem: '=',
      items: '=',
      selectedItems: '=ngModel'
    },
    template: '<select ng-model="selectedItems" multiple="multiple" style="height: 140px; width: 250px;"' +
      'ng-options="item.id as item.name group by item.model for item in items | orderBy:\'name\'">' +
      '<option>--</option> </select>',
    link: function(scope, element, attrs) {
      scope.$watchCollection('currentItem', function() {
        console.log(JSON.stringify(scope.currentItem));
      });
      scope.$watch('name', function() {
        console.log(JSON.stringify(scope.name));
      });
    }
  }
})

 app.controller('MainCtrl', function($scope) {
  $scope.user = 'World';

  $scope.addItem = function() {
    $scope.items.push({
      id: $scope.id,
      name: $scope.name,
      model: $scope.model
    });
    $scope.currentItem = {};
    $scope.currentItem.id = $scope.id;
    $scope.currentItem.name = $scope.name;
    $scope.currentItem.model = $scope.model;
  }

  $scope.selectedItems = ["allItems"];

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
  }]
});

지시문에 대한 자세한 설명은 항상 AngularJs 설명서를 참조하십시오.


답변

 scope: false
 transclude: false

그리고 당신은 같은 범위를 가질 것입니다 (부모 요소와 함께)

$scope.$watch(...

이 두 가지 옵션 범위 및 변환에 따라 상위 범위에 액세스하는 방법에는 여러 가지가 있습니다.


답변

한 번 사용한 트릭은 다음과 같습니다. “더미”지시문을 작성하여 상위 범위를 보유하고 원하는 지시문 외부에 배치하십시오. 다음과 같은 것 :

module.directive('myDirectiveContainer', function () {
    return {
        controller: function ($scope) {
            this.scope = $scope;
        }
    };
});

module.directive('myDirective', function () {
    return {
        require: '^myDirectiveContainer',
        link: function (scope, element, attrs, containerController) {
            // use containerController.scope here...
        }
    };
});

그리고

<div my-directive-container="">
    <div my-directive="">
    </div>
</div>

아마도 가장 우아한 해결책은 아니지만 일을 마쳤습니다.


답변

ES6 클래스 및 ControllerAs구문을 사용하는 경우 약간 다른 작업을 수행해야합니다.

아래 스 니펫을 참조 하여 상위 HTML에 사용 된 상위 컨트롤러 vmControllerAs값입니다.

myApp.directive('name', function() {
  return {
    // no scope definition
    link : function(scope, element, attrs, ngModel) {

        scope.vm.func(...)


답변

모든 것을 시도한 후에 마침내 해결책을 찾았습니다.

템플릿에 다음을 배치하십시오.

{{currentDirective.attr = parentDirective.attr; ''}}

현재 범위에 액세스하려는 상위 범위 속성 / 변수 만 작성합니다.

또한 ; ''명령문 끝에서 템플릿에 출력이 없는지 확인해야합니다. (Angular는 모든 명령문을 평가하지만 마지막 명령문 만 출력합니다).

약간 해 키지 만 몇 시간의 시행 착오 후에 일을합니다.


답변