2013-03-13 2 views
18

anugularJS를 사용하여 n 수준의 계층 적 순서가 지정되지 않은 목록을 생성하려고 시도 했으므로 성공적으로 수행 할 수있었습니다. 하지만 이제 지시문과 컨트롤러 사이에 범위 문제가 있습니다. 지시문 템플릿에서 ng-click이라는 함수 내에서 부모의 scope 속성을 변경해야합니다.angularJS를 사용하여 지시문과 컨트롤러 사이의 범위를 올바르게 바인딩하는 방법

http://jsfiddle.net/ahonaker/ADukg/2046/을 참조하십시오 - 여기 JS

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

//myApp.directive('myDirective', function() {}); 
//myApp.factory('myService', function() {}); 

function MyCtrl($scope) { 
    $scope.itemselected = "None"; 
    $scope.organizations = { 
     "_id": "SEC Power Generation", 
     "Entity": "OPUNITS", 
     "EntityIDAttribute": "OPUNIT_SEQ_ID", 
     "EntityID": 2, 
     "descendants": ["Eastern Conf Business Unit", "Western Conf Business Unit", "Atlanta", "Sewanee"], 
     children: [{ 
      "_id": "Eastern Conf Business Unit", 
      "Entity": "", 
      "EntityIDAttribute": "", 
      "EntityID": null, 
      "parent": "SEC Power Generation", 
      "descendants": ["Lexington", "Columbia", "Knoxville", "Nashville"], 
      children: [{ 
       "_id": "Lexington", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 10, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Columbia", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 12, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Knoxville", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 14, 
       "parent": "Eastern Conf Business Unit" 
      }, { 
       "_id": "Nashville", 
       "Entity": "OPUNITS", 
       "EntityIDAttribute": "OPUNIT_SEQ_ID", 
       "EntityID": 4, 
       "parent": "Eastern Conf Business Unit" 
      }] 
     }] 
    }; 

    $scope.itemSelect = function (ID) { 
     $scope.itemselected = ID; 
    } 
} 

app.directive('navtree', function() { 
    return { 
     template: '<ul><navtree-node ng-repeat="item in items" item="item" itemselected="itemselected"></navtree-node></ul>', 
     restrict: 'E', 
     replace: true, 
     scope: { 
      items: '=' 
     } 
    }; 
}); 

app.directive('navtreeNode', function ($compile) { 
    return { 
     restrict: 'E', 
     template: '<li><a ng-click="itemSelect(item._id)">{{item._id}} - {{itemselected}}</a></li>', 
     scope: { 
      item: "=", 
      itemselected: '=' 
     }, 
     controller: 'MyCtrl', 
     link: function (scope, elm, attrs) { 
      if ((angular.isDefined(scope.item.children)) && (scope.item.children.length > 0)) { 
       var children = $compile('<navtree items="item.children"></navtree>')(scope); 
       elm.append(children); 
      } 
     } 
    }; 
}); 

그리고 여기에 HTML 목록은 모델에서 생성

<div ng-controller="MyCtrl"> 
    Selected: {{itemselected}} 

    <navtree items="organizations.children"></navtree> 
</div> 

주입니다. ng-click은 부모 범위 속성 (itemselected)을 설정하는 함수를 호출하지만 변경은 로컬에서만 발생합니다. 예상되는 동작은 항목을 클릭 할 때 "Selected : None"이 "Selected : xxx"로 변경되어야한다는 것입니다. 여기서 xxx는 클릭 된 항목입니다.

부모 범위와 지시문 사이의 속성을 적절하게 바인딩하지 않습니까? 부모 범위에 속성 변경을 전달하려면 어떻게해야합니까?

희망이 있습니다.

미리 도움을 청하십시오.

+1

Welcome to StackOverflow! 끝 부분의 실제 질문 형태로 다시 말하면 게시물이 향상 될 수 있습니다. –

답변

18

이 작업 바이올린에 봐주십시오, 내가 무슨 짓을

http://jsfiddle.net/eeuSv/는 해당 컨트롤러에 정의 된 멤버 함수를 navtree-node 지시어 내 부모 컨트롤러가 필요하고, 전화를했다. 구성원 함수는 setSelected입니다. 그 this.setSelected이 아니라 $scope.setSelected이 아닙니다. 그런 다음 navtree-node 범위 방법 itemSelect을 정의하십시오. 앵커 태그를 클릭하면 navtree-node 범위의 itemSelect 메서드가 호출됩니다. 이 inturn은 컨트롤러 멤버 메서드 setSelected을 호출하여 선택한 ID를 전달합니다.

scope.itemSelect = function(id){ myGreatParentControler.setSelected(id) }

+1

감사합니다 rajkamal .... 나는 그것을 작동했습니다. 나는 아직도'require : "^ ngController"'ngController는 어디에서 왔습니까? – user2165994

+3

일반적으로'require : "^ ngController"문은 컨트롤러가 정의 된 부모 요소를 찾습니다. 'navtree' 위에 컨트롤러'MyCtrl'을 정의 했으므로 네 번째 매개 변수로 nav-node 지시문의 링킹 함수로 전달됩니다. 이에 대한 내용은 http://docs.angularjs.org/guide/directive의 Directive Definition Object heading에서 볼 수 있습니다. 여기서 그들은 옵션을 정의했습니다. – rajkamal

+3

+1. 좋은. 다른 사람이'require : '^ ngController'를 사용하는 예제를 보지 못했습니다. 필자가 보았던 다른 모든 예제에는 ngModel 또는 다른 지시문의 컨트롤러가 필요합니다. –

3

각 지시문이 자신의 범위를 만들기 때문일 수 있습니다 (실제로 지시를 내리면됩니다).
here 지시어, 특히 "지시문 작성 (긴 버전)"장을 참조하십시오.

범위 -로 설정 한 경우 : - 다음 새 범위이 지침 생성됩니다

사실. 같은 요소에있는 개의 지시문이 새 범위를 요청하면 새 범위가 만들어집니다. 새 범위 규칙은 템플릿의 루트가 항상 범위를 갖기 때문에 템플릿의 루트 에 적용되지 않습니다.

{} (개체 해시) - 새 '격리'범위가 생성됩니다. '격리'범위는 원래 범위 인 이 프로토 타입 적으로 상속되지 않는다는 점에서 정상 범위와 다릅니다. 이는 실수로 읽을 수 없거나 부모 범위에서 데이터를 수정하지 말아야하는 재사용 가능한 구성 요소를 만들 때 유용합니다.

그래서 각 지시문에는 '격리 된'범위가 있으므로 변경 사항은 MyCtrl 범위에 반영되지 않습니다.

그런 이유로 클릭은 로컬 변수 인 '$scope.itemselected'만 변경하고 '모두'는 변경하지 않습니다.

+0

감사합니다 maxdec,하지만 지시문을 재귀 적으로 계층 구조를 걸을 수있는 자체 범위가 필요합니다. 두 가지 장점을 모두 활용하려면 계층 구조의 로컬 범위와 다른 속성의 부모 범위에 대한 액세스 권한을 얻으십시오. 나는 당신이 광고 nauseam에 링크 된 문서를 읽었으며, 특히 "= or = attr - 로컬 범위 속성과 부모 범위 속성 사이의 양방향 바인딩을 설정하는 것에 대해 이해했다"고 생각했습니다. "&"모든 변경 사항 to parentModel는 localModel에 반영되어 localModel의 변경은 parentModel에 반영됩니다. 그러나 나는이 일을 할 수 없다. – user2165994

+0

@ user2165994, "두 가지 장점을 모두 얻으려면 어떻게해야합니까? 계층 구조에 대한 로컬 범위와 다른 속성에 대한 부모 범위에 대한 액세스"? -'scope : true'를 사용하십시오. 귀하의 지침은 자신의 범위를 얻을 것이다 [프로토 타입 상속] (http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs/14049482#14049482)를 부모 범위에서 삭제합니다. 따라서 자신의 속성 및/또는 참조 상위 속성을 정의 할 수 있습니다. 상위 속성에 작성해야하는 경우 기본 속성 ('prop1')이 아니라 객체 속성 (예 :'model.prop1')인지 확인하십시오. –

11

Maxdec이 맞습니다. 이는 범위 지정과 관련이 있습니다. 안타깝게도, 이것은 AngularJS 문서가 초보자에게 잘못 전달 될 수있을 정도로 복잡합니다.

경고 : 경고 :이 설명을 시도 할 때 약간 장시간을 기다리고 있습니다. 코드를보고 싶다면 JSFiddle으로 이동하십시오.또한 AngularJS에 대해 배울 때 귀중한 동영상 인 egghead.io을 발견했습니다.

다음은 문제점에 대한 이해입니다. 디렉티브 (navtree, navitem)의 계층 구조가 있으며 navitem의 정보를 "상위 트리"에서 루트 컨트롤러로 전달하려고합니다. 일반적으로 잘 작성된 Javascript와 마찬가지로 AngularJS는 변수 범위에 대해 엄격하게 설정되므로 실수로 페이지에서 실행되는 다른 스크립트도 엉망으로 작성하지 않습니다.

을 할 수 각도에서 특수 구문 (&)이있다 둘 모두 분리 범위를 만들고 부모 범위에서 함수를 호출 :

// in your directive 
scope: { 
    parentFunc: '&' 
} 

지금까지 너무 좋아. 이 지침의 여러 수준이있을 때 당신은 기본적으로 다음을 수행하기를 원하기 때문에 상황이, 까다로운 :

  1. 모델
  2. 미드 레벨 지시자에게 변수를 받아들이는 루트 컨트롤러의 기능을 가지고 업데이트
  3. 루트 컨트롤러

문제가 통신 할 수

  • 아이 수준의 지시어는, 아이 수준의 지시문은 루트 컨트롤러를 볼 수 없습니다. 내 이해는 다음과 같이 행동하는 지시 구조에서 "체인"을 설정해야한다는 것입니다 : 루트 컨트롤러에 함수를 반환하는 함수가 있습니다 (루트보기 컨트롤러 범위) :

    $scope.selectFunctionRoot = function() { 
        return function (ID) { 
         $scope.itemselected = ID; 
        } 
    } 
    

    둘째 :은 다음과 같은 것을 반환이 아이에게 전달합니다 자신의 선택 기능()의 가지고 중간 수준의 지침을 설정합니다. 에서 :

    // in the link function of the mid-level directive. the 'navtreelist' 
    scope.selectFunctionMid = function() { 
        // if we don't capture our mid-level scope, then when we call the function in the navtreeNode it won't be able to find the mid-level-scope's functions    
        _scope = scope; 
        return function (item_id) { 
         console.log('mid'); 
         console.log(item_id); 
    
         // this will be the "root" select function 
         parentSelectFunction = _scope.selectFunction(); 
         parentSelectFunction(item_id); 
        }; 
    }; 
    

    셋째,이 코드가 실제로 실행될 때 때문에 우리는 중간 수준의 지침의 범위를 저장해야하는 방법, 그것은 아이 수준 지시의 맥락에있을 것입니다주의 사항 여기서, fork of your JSFiddle으로 업데이트

    // in 'navtreeNode' link function 
    scope.childSelect = function (item_id) { 
        console.log('child'); 
        console.log(item_id); 
    
        // this will be the "mid" select function 
        parentSelectFunction = scope.selectFunction(); 
        parentSelectFunction(item_id); 
    }; 
    

    : 아이 레벨 지침 (navtreeNode)는, 차례로, 모든 방법 루트 컨트롤러에 "체인을 불러"하는 지역 함수를 호출 ng-click에 기능을 결합 코드에 주석이 있습니다.

  • +0

    멋진 anwser! 나는 뭔가를 배웠다. – maxdec

    +0

    컨트롤러와 지시어 사이의 범위의 양방향 바인딩을 사용하여 공통 함수를 공유하는 데 대한 탁월한 답. 감사! – Alex