在AngularJS中,如何从自定义指令中访问父作用域?

我正在寻找任何方式访问“父”范围内的指令。范围、排除、要求、从上面传入变量(或范围本身)等的任何组合。我完全愿意竭尽全力,但我想避免一些完全俗气或无法维护的东西。例如,我知道我现在可以通过从preLink参数中获取$scope并遍历它的$sibling作用域来找到概念上的“父”。

我真正想要的是能够$watch父作用域中的表达式。如果我能做到这一点,那么我就能完成我在这里想做的事情: AngularJS -如何渲染变量的部分? < / p >

重要提示是指令必须在相同的父作用域内可重用。因此,默认行为(范围: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?
}
}
}
};
});
272372 次浏览
 scope: false
transclude: false

你会有相同的作用域(父元素)

$scope.$watch(...

有很多方法来访问父作用域取决于这两个选项scope&transclude。

看到AngularJS中作用域原型/原型继承的细微差别是什么?

总结一下:一个指令访问它的父作用域($parent)的方式取决于该指令创建的作用域的类型:

  1. default (scope: false) -该指令不创建新的作用域,因此这里没有继承。该指令的作用域与父/容器的作用域相同。在link函数中,使用第一个参数(通常是scope)。

  2. scope: true -该指令创建了一个新的子作用域,原型继承自父作用域。在父作用域中定义的属性可用于指令scope(因为原型继承)。只是要注意写入一个基本作用域属性——这将在指令作用域上创建一个新属性(隐藏/阴影同名的父作用域属性)。

  3. scope: { ... } -该指令创建了一个新的隔离/隔离作用域。它不典型地继承父作用域。你仍然可以使用$parent访问父作用域,但通常不建议这样做。相反,你应该使用=@&符号,通过在使用指令的同一元素上的附加属性来指定该指令需要哪些父作用域属性(和/或函数)。

  4. transclude: true -该指令创建一个新的&;transcluded"子作用域,通常从父作用域继承。如果该指令还创建了一个隔离作用域,则transclude作用域和隔离作用域是兄弟作用域。每个作用域的$parent属性引用相同的父作用域。
    Angular v1.3更新:如果该指令还创建了一个隔离作用域,则transclude作用域现在是隔离作用域的子作用域。透光作用域和隔离作用域不再是兄弟作用域。被传输作用域的$parent属性现在引用隔离作用域。

上面的链接有这四种类型的例子和图片。

你不能访问指令的compile函数中的作用域(如这里提到的:https://github.com/angular/angular.js/wiki/Dev-Guide:-Understanding-Directives)。你可以在link函数中访问该指令的作用域。

看:

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

这里有一个我曾经用过的技巧:创建一个“dummy”指令来保存父范围,并将其放置在所需指令之外的某个地方。喜欢的东西:

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>

也许不是最优雅的解决方案,但它完成了任务。

访问控制器方法是指从指令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="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"
}]


});

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="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"
}]
});

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="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"
}]
});

working 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"
}]


});

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="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"
}]
});

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

选项6:通过$watch$watchCollection 在上述所有示例中,items是双向绑定,如果父作用域中的项被修改,指令中的项也将反映这些更改

如果你想从父范围观察其他属性或对象,你可以使用下面给出的$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="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。模块(“恰好”,[]);

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文档来获得关于指令的详细解释。

如果你使用ES6类和ControllerAs语法,你需要做一些稍微不同的事情。

请参阅下面的代码片段,并注意vm是父控制器的ControllerAs值,就像在父HTML中使用的那样

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


scope.vm.func(...)

尝试了一切办法,我终于想出了一个解决办法。

只需在模板中放置以下内容:

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

它只是将您想要访问的父作用域属性/变量写入当前作用域。

还要注意语句末尾的; '',它是为了确保模板中没有输出。(Angular计算每条语句,但只输出最后一条)。

这有点俗气,但经过几个小时的试验和错误,它可以工作。