Angular 1.6 组件包含范围
Angular 1.6 component transclusion scope
我正在尝试弄清楚如何将数据导入 Angular 1.6.4 中的组件嵌入。该场景有一个组件、一个指令(尚未重写为组件)和一个用于组件间通信的服务。
angular.module('app')
.service('svc', function() {
this.connector = {};
})
.directive('first', ['svc', function($svc) { return {
restrict: 'E',
scope: { 'id': '@' },
template: '<button ng-click="GetData()">get data</button>',
controller: ['$scope', 'svc', function($scope, $svc) {
$scope.connector = { data: [] };
$svc.connector[$scope.id] = $scope.connector;
$scope.GetData = function() {
// This is a mock-up; I'm really doing a REST call.
$scope.connector.data = [
{id: 0, name: 'one'},
{id: 1, name: 'two'}
];
};
}]
}; }])
.component('second', {
bindings: { parent: '@firstid' },
transclude: true,
template: '<ng-transclude></ng-transclude>',
controller: ['svc', function($svc) {
this.data = $svc.connector[this.parent];
// Not sure what to do here
}]
})
;
我的 HTML 看起来像这样:
<first id="first-thing"></first>
<second firstid="first-thing">
Where I expect my data to be: {{$ctrl | json}}<br/>
... but maybe here: {{$ctrl.$parent | json}}<br/>
... or even here: {{$parent | json}}<br/>
<div ng-repeat="item in $ctrl.data">
<p>Output: {{item.id}}/{{item.name}}</p>
</div>
</second>
这些可能没有嵌套 require
,这就是为什么我使用服务来存储我的数据; <first><second></second></first>
不是一个选项。我可以在必要时使用一些 $onInit
解决方法来管理从组件控制器内的服务获取数据。我已经检查过,该服务在正确的时间包含正确的数据。为了组件重用,我需要controller来嵌入内容。
Batarang 列出了我所有的瞄准镜。该指令有一个范围,$id
6(页面上还有其他内容),正如预期的那样。正如预期的那样,该组件的作用域为 $id
7。这些示波器包含基于我放入其中的内容和我所期望的正确数据。
我的问题是我有一个额外的作用域,$id
8。它似乎是被嵌入的作用域,它是 6 和 7 的兄弟(它们是 $id
5 上的对等体,我的页面控制器)。正如我在 HTML snark 中指出的那样,我希望组件嵌入存在于 7 中。如果 8 是 7 的子范围,我会很好,但它是一个断开连接的兄弟。我尝试了额外的绑定,但我无法让它们填充,所以它们只是抛出。我显然做错了什么,因为我得到的是 pre-1.3 用于包含范围继承的模型。
有人可以告诉我我在哪里误入歧途或者至少指出正确的解决方案吗?
我已经弄明白了。顺便提一下,根据 Internet 上的文献,我正在做一些我可能不应该做的事情。我理解 Angular 的作者试图在链中隔离作用域的出发点,但我不同意该模型,至少对于包含而言。
angular.module('app')
.service('svc', function() {
this.connector = {};
})
.directive('first', ['svc', function($svc) { return {
restrict: 'E',
scope: { 'id': '@' },
template: '<button ng-click="GetData()">get data</button>',
controller: ['$scope', 'svc', function($scope, $svc) {
$scope.connector = { data: [] };
$svc.connector[$scope.id] = $scope.connector;
$scope.GetData = function() {
// This is a mock-up; I'm really doing a REST call.
$scope.connector.data = [
{id: 0, name: 'one'},
{id: 1, name: 'two'}
];
$scope.connector.data.Update($scope.connector.data);
};
}]
}; }])
.component('second', {
bindings: { parent: '@firstid' },
transclude: true,
template: '<ng-transclude></ng-transclude>',
controller: ['$element', '$transclude', '$compile', 'svc', function($element, $transclude, $compile, $svc) {
this.$onInit = () => { angular.extend(this, $svc.connector[this.parent]; };
var parentid = $element.attr('firstid');
$transclude((clone, scope) => {
$svc.connector[parentid].Update = (data) => {
angular.extend(scope, data);
$element.append($compile(clone)(scope));
};
});
}]
})
;
工作原理
这本质上是手动嵌入。网上有太多关于手动嵌入的例子,人们手动修改 DOM 。我不完全理解为什么有些人认为这是个好主意。我们跳过了很多环节,将我们的标记 (HTML) 从我们的格式 (CSS) 从我们的代码 (Angular directives/components) 从我们的业务逻辑 (Angular services/factories/providers), 所以我不会回去把标记放在我的代码中。
我在 Gustavo Henke 的 Angular 问题上发现了 this article and a comment,它使用 $transclude
内的范围来注册回调。有了这些关键信息,我想我可以做更多的范围操作。
$transclude
中的代码似乎在摘要循环之外。这意味着它内部的任何触摸都不会收到自动更新。幸运的是,我可以控制数据的更改事件,所以我推动了这个回调。在回调中,数据被更改并且元素被重新编译。用于在服务中定位回调的键尚未从控制器标签绑定,因此必须手动从属性中检索它。
为什么这样不好
组件不应修改其自身范围之外的数据。我具体在做 not-that。在我看来,Angular 似乎没有更合适的原语来执行此操作而不破坏其他一些更重要的保持完整的问题。
我认为这里有一个 "memory leak,",也就是说我的元素和范围在每个更新周期都没有被正确处理。我使用的数据很少,它仅由用户直接通过节流进行更新,并且在管理界面上;我不介意泄漏一点内存,我不希望用户在页面上停留足够长的时间来产生影响。
我的代码都希望事物位于正确的位置并在标记中命名正确的事物。我的真实代码的行数大约是这行的四倍,我正在检查是否有错误或遗漏。这不是 Angular 方式,这意味着我可能做错了什么。
学分
如果没有 Telerik article,我现在可能会坐在墙上更血腥的标记旁边。
感谢 Ben Lesh 关于 $compile
的 comprehensive post 以及关于不应如何使用它的适当免责声明。
Todd Motto 帮助了很多人如何在 his post on upgrading to 1.6. As one may expect, the Angular documentation on components 中编写一个像样的 Angular 1.x 组件,除了提供具体的指针来确切地说明什么。
在 AngularJS issue 7842 的底部有一些信息做类似的事情,甚至可能比我有更好的方法来更适当地管理范围数据。
我正在尝试弄清楚如何将数据导入 Angular 1.6.4 中的组件嵌入。该场景有一个组件、一个指令(尚未重写为组件)和一个用于组件间通信的服务。
angular.module('app')
.service('svc', function() {
this.connector = {};
})
.directive('first', ['svc', function($svc) { return {
restrict: 'E',
scope: { 'id': '@' },
template: '<button ng-click="GetData()">get data</button>',
controller: ['$scope', 'svc', function($scope, $svc) {
$scope.connector = { data: [] };
$svc.connector[$scope.id] = $scope.connector;
$scope.GetData = function() {
// This is a mock-up; I'm really doing a REST call.
$scope.connector.data = [
{id: 0, name: 'one'},
{id: 1, name: 'two'}
];
};
}]
}; }])
.component('second', {
bindings: { parent: '@firstid' },
transclude: true,
template: '<ng-transclude></ng-transclude>',
controller: ['svc', function($svc) {
this.data = $svc.connector[this.parent];
// Not sure what to do here
}]
})
;
我的 HTML 看起来像这样:
<first id="first-thing"></first>
<second firstid="first-thing">
Where I expect my data to be: {{$ctrl | json}}<br/>
... but maybe here: {{$ctrl.$parent | json}}<br/>
... or even here: {{$parent | json}}<br/>
<div ng-repeat="item in $ctrl.data">
<p>Output: {{item.id}}/{{item.name}}</p>
</div>
</second>
这些可能没有嵌套 require
,这就是为什么我使用服务来存储我的数据; <first><second></second></first>
不是一个选项。我可以在必要时使用一些 $onInit
解决方法来管理从组件控制器内的服务获取数据。我已经检查过,该服务在正确的时间包含正确的数据。为了组件重用,我需要controller来嵌入内容。
Batarang 列出了我所有的瞄准镜。该指令有一个范围,$id
6(页面上还有其他内容),正如预期的那样。正如预期的那样,该组件的作用域为 $id
7。这些示波器包含基于我放入其中的内容和我所期望的正确数据。
我的问题是我有一个额外的作用域,$id
8。它似乎是被嵌入的作用域,它是 6 和 7 的兄弟(它们是 $id
5 上的对等体,我的页面控制器)。正如我在 HTML snark 中指出的那样,我希望组件嵌入存在于 7 中。如果 8 是 7 的子范围,我会很好,但它是一个断开连接的兄弟。我尝试了额外的绑定,但我无法让它们填充,所以它们只是抛出。我显然做错了什么,因为我得到的是 pre-1.3 用于包含范围继承的模型。
有人可以告诉我我在哪里误入歧途或者至少指出正确的解决方案吗?
我已经弄明白了。顺便提一下,根据 Internet 上的文献,我正在做一些我可能不应该做的事情。我理解 Angular 的作者试图在链中隔离作用域的出发点,但我不同意该模型,至少对于包含而言。
angular.module('app')
.service('svc', function() {
this.connector = {};
})
.directive('first', ['svc', function($svc) { return {
restrict: 'E',
scope: { 'id': '@' },
template: '<button ng-click="GetData()">get data</button>',
controller: ['$scope', 'svc', function($scope, $svc) {
$scope.connector = { data: [] };
$svc.connector[$scope.id] = $scope.connector;
$scope.GetData = function() {
// This is a mock-up; I'm really doing a REST call.
$scope.connector.data = [
{id: 0, name: 'one'},
{id: 1, name: 'two'}
];
$scope.connector.data.Update($scope.connector.data);
};
}]
}; }])
.component('second', {
bindings: { parent: '@firstid' },
transclude: true,
template: '<ng-transclude></ng-transclude>',
controller: ['$element', '$transclude', '$compile', 'svc', function($element, $transclude, $compile, $svc) {
this.$onInit = () => { angular.extend(this, $svc.connector[this.parent]; };
var parentid = $element.attr('firstid');
$transclude((clone, scope) => {
$svc.connector[parentid].Update = (data) => {
angular.extend(scope, data);
$element.append($compile(clone)(scope));
};
});
}]
})
;
工作原理
这本质上是手动嵌入。网上有太多关于手动嵌入的例子,人们手动修改 DOM 。我不完全理解为什么有些人认为这是个好主意。我们跳过了很多环节,将我们的标记 (HTML) 从我们的格式 (CSS) 从我们的代码 (Angular directives/components) 从我们的业务逻辑 (Angular services/factories/providers), 所以我不会回去把标记放在我的代码中。
我在 Gustavo Henke 的 Angular 问题上发现了 this article and a comment,它使用 $transclude
内的范围来注册回调。有了这些关键信息,我想我可以做更多的范围操作。
$transclude
中的代码似乎在摘要循环之外。这意味着它内部的任何触摸都不会收到自动更新。幸运的是,我可以控制数据的更改事件,所以我推动了这个回调。在回调中,数据被更改并且元素被重新编译。用于在服务中定位回调的键尚未从控制器标签绑定,因此必须手动从属性中检索它。
为什么这样不好
组件不应修改其自身范围之外的数据。我具体在做 not-that。在我看来,Angular 似乎没有更合适的原语来执行此操作而不破坏其他一些更重要的保持完整的问题。
我认为这里有一个 "memory leak,",也就是说我的元素和范围在每个更新周期都没有被正确处理。我使用的数据很少,它仅由用户直接通过节流进行更新,并且在管理界面上;我不介意泄漏一点内存,我不希望用户在页面上停留足够长的时间来产生影响。
我的代码都希望事物位于正确的位置并在标记中命名正确的事物。我的真实代码的行数大约是这行的四倍,我正在检查是否有错误或遗漏。这不是 Angular 方式,这意味着我可能做错了什么。
学分
如果没有 Telerik article,我现在可能会坐在墙上更血腥的标记旁边。
感谢 Ben Lesh 关于 $compile
的 comprehensive post 以及关于不应如何使用它的适当免责声明。
Todd Motto 帮助了很多人如何在 his post on upgrading to 1.6. As one may expect, the Angular documentation on components 中编写一个像样的 Angular 1.x 组件,除了提供具体的指针来确切地说明什么。
在 AngularJS issue 7842 的底部有一些信息做类似的事情,甚至可能比我有更好的方法来更适当地管理范围数据。