ディレクティブ内で「親」スコープにアクセスする方法を探しています。スコープ、transclude、require、上からの変数(またはスコープ自体)の受け渡しなど、あらゆる組み合わせ。逆向きに曲がっても構わないと思っています。たとえば、preLinkパラメータから$scope
を取得し、その$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
)スコープにアクセスする方法は、そのディレクティブが作成するスコープの種類によって異なります。
default(scope: false
) - ディレクティブは新しいスコープを作成しないので、ここでは継承はありません。ディレクティブの有効範囲は、親/コンテナーと同じ有効範囲です。リンク関数では、最初のパラメータ(通常はscope
)を使用します。
scope: true
- ディレクティブは、プロトタイプ的に親スコープから継承する新しい子スコープを作成します。親スコープで定義されているプロパティは、ディレクティブscope
で使用できます(プロトタイプ継承のため)。プリミティブスコーププロパティへの書き込みに注意してください - それはディレクティブスコープ上に新しいプロパティを作成します(それは同じ名前の親スコーププロパティを隠し/隠します)。
scope: { ... }
- ディレクティブは新しい分離/分離スコープを作成します。プロトタイプ的には親スコープを継承しません。 $parent
を使って親スコープにアクセスすることはできますが、これは通常お勧めできません。代わりに、=
、@
、および&
の表記法を使用して、ディレクティブが使用されているのと同じエレメントの追加属性を介して、ディレクティブが必要とする親スコープのプロパティ(および/または関数)を指定します。
transclude: true
- ディレクティブはプロトタイプ的に親スコープから継承した新しい「透明な」子スコープを作成します。ディレクティブが独立スコープも作成する場合、変換されたスコープと独立スコープは兄弟です。各スコープの$parent
プロパティは、同じ親スコープを参照しています。
Angular v1.3 update :ディレクティブが独立スコープも作成する場合、トランススコープされたスコープは独立スコープの子になります。遮られて孤立したスコープはもう兄弟ではありません。変換されたスコープの$parent
プロパティは現在、独立スコープを参照しています。
上記のリンクには、4種類すべての例と写真があります。
ディレクティブのコンパイル関数でスコープにアクセスすることはできません(ここに記載されているように: https://github.com/angular/angular.js/wiki/Undestanding- Direction )。リンク関数でディレクティブの有効範囲にアクセスできます。
視聴中:
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ディレクティブスコープから親スコープメソッドにアクセスする場合は、もう少し作業が必要です。
孤立したディレクティブスコープから親スコープメソッドを呼び出したり、親スコープ変数を監視したりするオプションはほとんどありません(以下にリストされているものよりも多い)(option#6特別)。
注これらの例ではlink function
を使用しましたが、要件に応じてdirective controller
も使用できます。
Option#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"
}]
});
working plnkr: http://plnkr.co/edit/rgKUsYGDo9O3tewL6xgr?p=preview
オプション#2。オブジェクトリテラルを介して、ディレクティブlink/scopeから
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
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"
}]
});
working 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"
}]
});
working plnkr: http://plnkr.co/edit/Jo6FcYfVXCCg3vH42BIz?p=preview
オプション#4。関数参照およびディレクティブlink/scopeから
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"
}]
});
working 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="[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"
}]
});
working plnkr: http://plnkr.co/edit/hNui3xgzdTnfcdzljihY?p=preview
オプション#6:$watch
および$watchCollection
を介して上記のすべての例でitems
の双方向バインディングであり、アイテムが親スコープで変更された場合、ディレクティブのアイテムも変更を反映します。
親スコープから他の属性またはオブジェクトを監視する場合は、以下に示すように$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="[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>
script 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(...
この2つのオプションscope&transcludeに応じて、親スコープにアクセスする方法はたくさんあります。
これが私がかつて使ったトリックです:親のスコープを保持して、それを目的のディレクティブの外側のどこかに置くための「ダミー」ディレクティブを作成します。何かのようなもの:
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
構文を使用している場合 は、少し異なることをする必要があります。
下記のスニペットを見て、vm
が親HTMLで使用される親ControllerのControllerAs
値であることに注意
myApp.directive('name', function() {
return {
// no scope definition
link : function(scope, element, attrs, ngModel) {
scope.vm.func(...)
すべてを試した結果、私はついに解決策を思いついた。
以下をテンプレートに入れるだけです。
{{currentDirective.attr = parentDirective.attr; ''}}
現在のスコープにアクセスしたい親スコープの属性/変数を書き込むだけです。
また、ステートメントの最後にある; ''
に注意してください。テンプレートに出力がないことを確認するためです。 (Angularはすべてのステートメントを評価しますが、最後のステートメントのみを出力します)。
ちょっとハックですが、数時間の試行錯誤の末、仕事は終わりました。