2013-03-13 65 views
18

我想用anugularJS生成一個n級的層次無序列表,並已能夠成功地這樣做。但現在,我在指令和控制器之間存在範圍問題。我需要在指令模板中通過ng-click調用的函數內更改父級的範圍屬性。如何正確地綁定指令和控制器之間的角度與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

歡迎來到StackOverflow!如果您在接近尾聲的時候以實際問題的形式來重述它,它可能會改進您的帖子。 – 2013-03-13 15:20:51

回答

18

請看看這方面的工作小提琴,http://jsfiddle.net/eeuSv/

我做什麼,要求navtree-node指令裏面的父控制器,並呼籲在控制器中定義的成員函數。 成員函數是setSelected。請注意,其this.setSelected而不是$scope.setSelected。 然後定義一個navtree-node示波器方法itemSelect。單擊定位標記時,它將調用navtree-node範圍上的itemSelect方法。該內容將調用控制器成員方法setSelected傳遞選定的ID。

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

+1

謝謝rajkamal ....我讓它工作。我仍然對'require'感到困惑:「ngController」'ngController從哪裏來? – user2165994 2013-03-13 19:47:18

+3

可以用一般的術語來說:'require:「^ ngController」'語句查找控制器上定義的父元素。由於我們在'navtree'上面定義了控制器'MyCtrl',所以它將作爲第四個參數傳遞給nav-node指令的鏈接函數。您可以在指令定義對象標題下的http://docs.angularjs.org/guide/directive中閱讀此內容。他們在這裏定義了選項。 – rajkamal 2013-03-13 20:05:31

+3

+1。尼斯。我還沒有看到其他人在使用'require:'^ ngController''的SO上給出了一個例子。我見過的所有其他示例都需要ngModel或其他指令的控制器。 – 2013-03-13 22:26:24

3

這可能是因爲每個指令創建了自己的範圍(實際上你告訴他們這樣做)。
您可以閱讀更多關於指令here,特別是「編寫指令(長版)」一章。

範圍 - 如果設置爲:

真正 - 那麼一個新的範圍將這個指令創建。如果 同一元素上的多個指令請求一個新範圍,則僅創建一個新的範圍 。新範圍規則不適用於模板的根 ,因爲模板的根始終獲取新的 範圍。

{}(object hash) - 然後創建一個新的'isolate'作用域。 「分離」範圍與正常範圍不同,因爲 原型從父範圍繼承。這在 創建可重用組件(不應該意外讀取)或 修改父範圍中的數據時非常有用。

因此,您所做的更改未反映在MyCtrl範圍內,因爲每個指令都有自己的「隔離」範圍。

這就是爲什麼點擊只會改變本地$scope.itemselected變量而不是它們的「全部」。

+0

感謝maxdec,但是指令需要有自己的範圍才能遞歸遍歷層次結構。我如何獲得兩全其美的結果:層次結構的本地範圍和其他屬性的父級範圍的訪問權限?我讀過你鏈接到ad dause的文檔,並認爲我理解了,特別是關於「=或= attr - 在本地範圍屬性和父範圍屬性之間設置雙向綁定」...「&」任何更改到parentModel將反映在localModel中,localModel中的任何更改都將反映在parentModel中。「但我無法完成這項工作。 – user2165994 2013-03-13 18:21:05

+0

@ user2165994,「我該如何獲得兩全其美:層次結構的本地範圍和訪問其他屬性的父範圍?」 - 使用'scope:true'。你的指令會得到它自己的作用域[prototypically inherits](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs/14049482#14049482 )從父範圍。所以你可以定義你自己的屬性,和/或引用父屬性。如果您需要寫入父屬性,請確保它是對象屬性(例如'model.prop1'),而不是基元('prop1')。 – 2013-03-13 22:29:18

11

Maxdec是對的,這與範圍界定有關。不幸的是,這是一個足夠複雜的情況,AngularJS文檔可能會導致初學者(比如我)錯誤的引導。

警告:在我試圖解釋這一點時,要讓自己有點囉嗦。如果您只想查看代碼,請轉至此JSFiddle。我還發現egghead.io視頻對於瞭解AngularJS非常有用。

下面是我對這個問題的理解:你有一個指令層次結構(navtree,navitem),並且你希望將信息從navitem「上樹」傳遞到根控制器。 AngularJS就像通常寫得很好的Javascript一樣,被設置爲嚴格限定變量的範圍,這樣就不會意外地搞亂頁面上運行的其他腳本。

有角中的一個特殊的語法(&),讓你倆創造一個隔離範圍,並調用父範圍功能:

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

三:在子層次指令(navtreeNode)功能綁定到ng-click調用本地功能,這將反過來,「調用鏈」一路到根控制器:

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

下面是更新fork of your JSFiddle,代碼中有評論。

+0

不錯的anwser!我學到了東西。 – maxdec 2013-03-13 21:48:09

+0

在控制器和指令之間使用範圍的雙向綁定來共享通用函數的優秀答案。謝謝! – Alex 2014-04-16 08:57:03

相關問題