2013-07-27 9 views
301

私はディレクティブ内の "親"スコープにアクセスする方法を探しています。スコープの任意の組み合わせ、transclude、必要、上から変数(またはスコープ自体)を渡すなど私は完全に後方に曲げて喜んでいるが、私は完全にハッキーまたは維持不能な何かを避けたい。たとえば、プレリンクのパラメータから$scopeを取り出し、それを反復して$siblingのスコープを使用して概念的な「親」を見つけることで、今すぐ行うことができます。AngularJSのカスタムスコープ*内のカスタムディレクティブ*内から親スコープにアクセスするには?

本当に欲しいのは、$watchの親スコープの式です。私はそれを行うことができるなら、私はこっち何をしようとしている達成することができます AngularJS - How to render a partial with variables?

重要な注意ディレクティブは、再使用可能なされなければならないことを同じ親スコープ内です。したがって、デフォルトの振る舞い(スコープ: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? 
       } 
      } 
     } 
    }; 
}); 

答えて

606

は、要約するとWhat are the nuances of scope prototypal/prototypical inheritance in AngularJS?

を参照してください:ディレクティブはその親($parent)スコープにアクセスする方法は、ディレクティブが作成スコープの種類によって異なります。

  1. デフォルト(scope: false) - ディレクティブがありません新しいスコープを作成するので、ここに継承はありません。ディレクティブのスコープは、親/コンテナと同じスコープです。リンク関数では、最初のパラメータ(通常scope)を使用します。

  2. scope: true - このディレクティブは、元のスコープからプロトタイプ的に継承する新しい子スコープを作成します。親スコープで定義されたプロパティは、scopeディレクティブ(プロトタイプの継承のため)で使用できます。プリミティブなスコーププロパティに書き込むだけで、ディレクティブスコープ(同じ名前の親スコーププロパティを隠す/隠す)で新しいプロパティを作成することに注意してください。

  3. scope: { ... } - ディレクティブは新しい分離/分離スコープを作成します。プロトタイプ的に親スコープを継承しません。 $parentを使用して親スコープにアクセスすることはできますが、これは通常は推奨されません。代わりに、=,@、および&表記を使用して、ディレクティブが使用されているのと同じ要素の追加の属性を使用して、ディレクティブに必要な親スコープのプロパティ(および/または関数)を指定する必要があります。

  4. transclude: true - このディレクティブは、新しい「継承された」子スコープを作成します。これはプロトタイプ的に親スコープを継承します。ディレクティブが分離スコープも作成する場合、分離されたスコープと分離スコープは兄弟です。各スコープの$parentプロパティは、同じ親スコープを参照します。
    角度v1.3の更新:ディレクティブでも分離スコープが作成されている場合、トランスコードされたスコープは分離スコープの子になりました。絡み合って分離したスコープはもはや兄弟ではありません。継承されたスコープの$parentプロパティは、分離スコープを参照するようになりました。

上記のリンクには、4つのタイプすべての例と写真があります。

ディレクティブのコンパイル機能のスコープにアクセスすることはできません(ここで述べるように:https://github.com/angular/angular.js/wiki/Understanding-Directives)。ディレクティブのスコープには、リンク機能でアクセスできます。

ウォッチング:1.と2.については

以上:通常、あなたはその後、$それを見属性を経由している親プロパティディレクティブのニーズ、指定:

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

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

あなたが見ているならあなたが@=を使用してディレクティブプロパティに付けた名前を見て、上記3.

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

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

(スコープを隔離):オブジェクトのプロパティをINGの、あなたは$の解析を使用する必要があります表記:

<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() { ... }); 
+1

THANKあなた、マーク。それは私が[変数で部分的にレンダリングする方法](http://stackoverflow.com/questions/17863732/angularjs-how-to-render-a-partial-with-variables)に投稿されたソリューションが本当にうまく機能することが分かります美しく。私をリンクさせるために本当に必要なのは、「HTMLを記述し、あなたの要素があなたが思っているng-controllerの中に入れ子になっていないことを認識するニュアンス。うわー...ルーキーミス。しかしこれはスコープを説明するあなたの他の(ずっと長い)答えに役立つ追加です。 – colllin

+0

@collin、あなたの問題を解決してうれしく思います。他の(今削除された)コメントにどのように反応するかがはっきりしていないので、私はあなたの問題を解決してうれしいです。 –

+0

スコープ内で実行できる作業は何ですか?$ watch( 'localName3'、function(){... [)]));) ' –

12
scope: false 
transclude: false 

、あなたは(親要素と)同じスコープを持ちます

$scope.$watch(... 

この2つのオプションスコープ&のtranscludeに応じて親スコープにアクセスします。

+0

はい、短い&甘い、そして正しい。彼らは親要素と同じスコープを共有しているように見えますが...同じ範囲で再利用することは不可能です。 http://jsfiddle.net/collindo/xqytH/ – colllin

+0

修正されたURL URL http://jsfiddle.net/colllin/xqytH/ – colllin

+2

何度も再利用可能なコンポーネントを書くときに分離されたスコープが必要なので、解決策はそれほど簡単ではありません –

7

これは私が一度使ったトリックです:親スコープを保持するための "ダミー"ディレクティブを作成し、それを望みのディレクティブの外側のどこかに置きます。何かのように:

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> 

多分ない最も優雅な解決策が、それは仕事が行われました。

44

コントローラメソッドへのアクセスとは、指示子controller/link/scopeから親スコープのメソッドにアクセスすることを意味します。

ディレクティブが親スコープを共有/継承している場合は、親スコープメソッドを呼び出すだけでかなり簡単です。

隔離された指示スコープから親スコープのメソッドにアクセスするには、少しだけ作業が必要です。

分離ディレクティブスコープまたはウォッチ親スコープ変数(オプション#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="[email protected]" 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="[email protected]" 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

を通じて、

作業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="[email protected]" 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="[email protected]" 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-モデルと結合双方向を通じて、あなたは親スコープの変数を更新することができます。。したがって、親スコープ関数を呼び出す必要がない場合があります。

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="[email protected]" 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$watchCollection を通じて、それはすべて上記の例でitemsのために2つの結合方法である項目が親スコープに変更された場合、ディレクティブ内の項目も変更を反映することになります。

あなたが親スコープから他の属性やオブジェクトを見たい場合は、あなたが

htmlの下に与えられたとして$watch$watchCollectionを使用していることを行うことができます

<!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="[email protected]" 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のマニュアルを参照して、ディレクティブの詳細な説明を参照できます。

+9

彼は彼の担当者のために懸命に働いています...彼の担当者にとっては難しいです...彼は彼の担当者のために懸命に働くので、あなたは彼を正しくアップアップしてください。 – slim

+7

downvoted - 回答内の貴重な情報は、その長さのためにアクセスできない – redress

+2

私は、利用可能なすべての選択肢と明確な分離で質問に答えました。私の意見では、あなたの目の前に大きな絵があるまで、短い答えはいつも役に立つとは限りません。 –

4

ES6クラスとControllerAsの構文を使用している場合は、少し異なる必要があります。

以下のスニペットを参照して、親HTMLで使用されるようvmが親コントローラのControllerAs値であることに注意してください

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

     scope.vm.func(...) 
0

はすべてを試みた、私は最終的に解決策を考え出しました。

ちょうどあなたのテンプレートに次のように配置します。

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

それはちょうどあなたが現在のスコープにアクセスしたい親スコープ属性/変数を書き込みます。

文の末尾には; ''があります。テンプレートに出力がないことを確認してください。 (Angularはすべてのステートメントを評価しますが、最後のステートメントのみを出力します)。

少しハッキリですが、数時間の試行錯誤の後、それは仕事をします。

関連する問題