从 AngularJS 中的指令访问 $scope
Accessing $scope From a Directive in AngularJS
由于我们缺乏使用 AngularJS 进行开发的专业知识,我们在开发过程中遇到了另一个障碍。
我们正在开发一个 Angular/Web API 应用程序,其中我们的页面仅包含一个交互式 SVG 图,当用户将鼠标悬停在 Angular 指令中的特定 SVG 标记上时,该图会显示数据.
应用程序中当前有两个自定义指令。
- 指令一 - 将 SVG 文件加载到网页中
- 指令二 - 添加 SVG 元素悬停 event/data 过滤器
指令一:
//directive loads SVG into DOM
angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
return {
restrict: 'A',
templateUrl: 'test.svg',
link: function (scope, element, attrs) {
var groups = element[0].querySelectorAll("g[id^='f3']")
angular.forEach(groups, function (g,key) {
var cubeElement = angular.element(g);
//Wrap the cube DOM element as an Angular jqLite element.
cubeElement.attr("cubehvr", "");
$compile(cubeElement)(scope);
})
}
}
}]);
SVG 图包含具有唯一标识符的标签,即:
<g id="f3s362c12"></g>
指令从与每个 SVG 标签 ID 相对应的注入服务加载 JSON 数据。
//filters json based on hover item
dataService.getData().then(function(data) {
thisData = data.filter(function (d) {
return d.seatId.trim() === groupId
});
如上所示,指令二还添加了悬停事件功能,可根据悬停的标签过滤 JSON 数据。
IE:如果用户将鼠标悬停在 上,指令中的过滤器将 return 这条 JSON 记录:
{"Id":1,
"empNum":null,
"fName":" Bun E.",
"lName":"Carlos",
...
"seatId":"f3s362c12 ",
"floor":3,
"section":"313 ",
"seat":"12 "}
指令二:
//SVG hover directive/filter match json to svg
angular.module("FFPA").directive('cubehvr', ['$compile', 'dataService', function ($compile, dataService) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
//id of group
scope.elementId = element.attr("id");
//alert(scope.elementId);
var thisData;
//function call
scope.cubeHover = function () {
//groupId is the id of the element hovered over.
var groupId = scope.elementId;
//filters json based on hover item
dataService.getData().then(function(data) {
thisData = data.filter(function (d) {
return d.seatId.trim() === groupId
});
//return data.seatId === groupId
scope.gData = thisData[0];
alert(thisData[0].fName + " " + thisData[0].lName + " " + thisData[0].deptId);
});
//after we get a match, we need to display a tooltip with save/cancel buttons.
$scope.empData = $scope.gData;
};
element.attr("ng-mouseover", "cubeHover()");
element.removeAttr("cubehvr");
$compile(element)(scope);
}
//,
//controller: function($scope, $element){
// $scope.empData = $scope.gData;
//}
}
}]);
我们现在面临的问题是(除了拥有最少的 Angular 经验和面临独特而困难的实施问题之外)我们正在尝试实施一种使用 div 标签和一个 angular 范围变量,当用户将鼠标悬停在 SVG 标签元素上时我们可以显示它(而不是下面的 Plunker POC link 中演示的 Javascript 警报)。
由于数据是由指令驱动的,并且指令已经将"cubehvr"作为参数:
angular.module("FFPA").directive('*cubehvr*', ['$compile', 'dataService', function ($compile, dataService)
我们被卡住了,因为我们不知道如何设置 HTML 页面范围指令或变量,从我们的第二个指令中这样说:
<div uib-popover="Last Name: {{empData.lName}}"
popover-trigger="'mouseenter'"
type="div"
class="btn btn-default">Tooltip
</div>
或者简单地说,这个:
<div emp-info></div>
div 工具提示将有 html 按钮调用 Web API 更新功能。
我们这里有一个缩小版的 POC Plunk:
还考虑使用 Angular Bootstrap UI 作为工具提示:
希望这是有道理的。
//编辑。我再次阅读了您的问题并仔细阅读了我的答案。我没有完全回答你的问题,因为它 multi-layered。现在我将解决您所有的问题并尝试回答它们:
- 将 $scope 传递给其他指令。
$scope 在 MVVM 设计模式中是 Model-View,它将模板(视图)和模型粘合在一起。理论上你可以将 $scope 传递给另一个指令,但我认为它是一个 anti-pattern.
directives.There 之间的通信至少有 4 种方法,您可以使用这些方法来传达您的指令:
将弹出窗口添加到 SVG 的 child。 Bootstrap 能够将弹出窗口添加到 body 而不是 parent 元素。它对 SVG 很有用:https://angular-ui.github.io/bootstrap/#!#popover
我重构了您的代码以使用两个指令,并将数据加载到控制器中。一个指令包装 popover,第二个指令传递数据,popover 现在也使用模板,所以它被编译为 angular:
var app = angular.module('FFPA', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
//controller
app.controller('myCtrl', function ($scope, dataService) {
$scope.test = 'test';
dataService.getData().then(function(data) {
$scope.dataset = data.reduce(function (obj, item) {
obj[item.seatId.trim()] = item;
item.fullName = item.fName + ' ' + item.lName;
return obj;
}, {});
});
});
angular.module('FFPA').service('dataService', function($http){
this.getData = function(){
return $http.get("data.json").then(
function(response){
return response.data;
}, function() {
return {err:"could not get data"};
}
);
}
});
//directive loads SVG into DOM
angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
return {
restrict: 'A',
templateUrl: 'test.svg',
scope: {
'dataset': '=svgFloorplan'
},
link: {
pre: function (scope, element, attrs) {
var groups = element[0].querySelectorAll("g[id^='f3']");
scope.changeName = function (groupId) {
if (scope.dataset[groupId] && scope.dataset[groupId].lastName.indexOf('changed') === -1) {
scope.dataset[groupId].lastName += ' changed';
}
}
groups.forEach(function(group) {
var groupId = group.getAttribute('id');
if (groupId) {
var datasetBinding = "dataset['" + groupId + "']";
group.setAttribute('svg-floorplan-popover', datasetBinding);
$compile(group)(scope);
}
});
}
}
}
}]);
angular.module('FFPA').directive('svgFloorplanPopover', ['$compile', function ($compile) {
return {
restrict: 'A',
scope: {
'person': '=svgFloorplanPopover'
},
link: function (scope, element, attrs) {
scope.changeName = function () {
if (scope.person && scope.person.fullName.indexOf('changed') === -1) {
scope.person.fullName += ' changed';
}
}
scope.htmlPopover = 'popoverTemplate.html';
element[0].setAttribute('uib-popover-template', "htmlPopover");
element[0].setAttribute('popover-append-to-body', 'true');
element[0].setAttribute('popover-trigger', "'outsideClick'");
element[0].querySelector('text').textContent += '{{ person.fullName }}';
element[0].removeAttribute('svg-floorplan-popover');
$compile(element)(scope);
}
}
}]);
你的 HTML body 现在看起来像:
<body style="background-color:#5A8BC8;">
<div ng-app="FFPA" ng-controller="myCtrl">
<div svg-floorplan="dataset"></div>
</div>
</body>
HTML 用于弹出窗口:
<div><button type="button" class="btn btn-default" ng-click="changeName()">{{ person.fullName }}</button></div>
这是工作中的 plunker:
http://plnkr.co/edit/uHgnZ1ZprZRDvL0uIkcH?p=preview
由于我们缺乏使用 AngularJS 进行开发的专业知识,我们在开发过程中遇到了另一个障碍。
我们正在开发一个 Angular/Web API 应用程序,其中我们的页面仅包含一个交互式 SVG 图,当用户将鼠标悬停在 Angular 指令中的特定 SVG 标记上时,该图会显示数据.
应用程序中当前有两个自定义指令。
- 指令一 - 将 SVG 文件加载到网页中
- 指令二 - 添加 SVG 元素悬停 event/data 过滤器
指令一:
//directive loads SVG into DOM
angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
return {
restrict: 'A',
templateUrl: 'test.svg',
link: function (scope, element, attrs) {
var groups = element[0].querySelectorAll("g[id^='f3']")
angular.forEach(groups, function (g,key) {
var cubeElement = angular.element(g);
//Wrap the cube DOM element as an Angular jqLite element.
cubeElement.attr("cubehvr", "");
$compile(cubeElement)(scope);
})
}
}
}]);
SVG 图包含具有唯一标识符的标签,即:
<g id="f3s362c12"></g>
指令从与每个 SVG 标签 ID 相对应的注入服务加载 JSON 数据。
//filters json based on hover item
dataService.getData().then(function(data) {
thisData = data.filter(function (d) {
return d.seatId.trim() === groupId
});
如上所示,指令二还添加了悬停事件功能,可根据悬停的标签过滤 JSON 数据。
IE:如果用户将鼠标悬停在 上,指令中的过滤器将 return 这条 JSON 记录:
{"Id":1,
"empNum":null,
"fName":" Bun E.",
"lName":"Carlos",
...
"seatId":"f3s362c12 ",
"floor":3,
"section":"313 ",
"seat":"12 "}
指令二:
//SVG hover directive/filter match json to svg
angular.module("FFPA").directive('cubehvr', ['$compile', 'dataService', function ($compile, dataService) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
//id of group
scope.elementId = element.attr("id");
//alert(scope.elementId);
var thisData;
//function call
scope.cubeHover = function () {
//groupId is the id of the element hovered over.
var groupId = scope.elementId;
//filters json based on hover item
dataService.getData().then(function(data) {
thisData = data.filter(function (d) {
return d.seatId.trim() === groupId
});
//return data.seatId === groupId
scope.gData = thisData[0];
alert(thisData[0].fName + " " + thisData[0].lName + " " + thisData[0].deptId);
});
//after we get a match, we need to display a tooltip with save/cancel buttons.
$scope.empData = $scope.gData;
};
element.attr("ng-mouseover", "cubeHover()");
element.removeAttr("cubehvr");
$compile(element)(scope);
}
//,
//controller: function($scope, $element){
// $scope.empData = $scope.gData;
//}
}
}]);
我们现在面临的问题是(除了拥有最少的 Angular 经验和面临独特而困难的实施问题之外)我们正在尝试实施一种使用 div 标签和一个 angular 范围变量,当用户将鼠标悬停在 SVG 标签元素上时我们可以显示它(而不是下面的 Plunker POC link 中演示的 Javascript 警报)。
由于数据是由指令驱动的,并且指令已经将"cubehvr"作为参数:
angular.module("FFPA").directive('*cubehvr*', ['$compile', 'dataService', function ($compile, dataService)
我们被卡住了,因为我们不知道如何设置 HTML 页面范围指令或变量,从我们的第二个指令中这样说:
<div uib-popover="Last Name: {{empData.lName}}"
popover-trigger="'mouseenter'"
type="div"
class="btn btn-default">Tooltip
</div>
或者简单地说,这个:
<div emp-info></div>
div 工具提示将有 html 按钮调用 Web API 更新功能。
我们这里有一个缩小版的 POC Plunk:
还考虑使用 Angular Bootstrap UI 作为工具提示:
希望这是有道理的。
//编辑。我再次阅读了您的问题并仔细阅读了我的答案。我没有完全回答你的问题,因为它 multi-layered。现在我将解决您所有的问题并尝试回答它们:
- 将 $scope 传递给其他指令。
$scope 在 MVVM 设计模式中是 Model-View,它将模板(视图)和模型粘合在一起。理论上你可以将 $scope 传递给另一个指令,但我认为它是一个 anti-pattern.
directives.There 之间的通信至少有 4 种方法,您可以使用这些方法来传达您的指令:
将弹出窗口添加到 SVG 的 child。 Bootstrap 能够将弹出窗口添加到 body 而不是 parent 元素。它对 SVG 很有用:https://angular-ui.github.io/bootstrap/#!#popover
我重构了您的代码以使用两个指令,并将数据加载到控制器中。一个指令包装 popover,第二个指令传递数据,popover 现在也使用模板,所以它被编译为 angular:
var app = angular.module('FFPA', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
//controller
app.controller('myCtrl', function ($scope, dataService) {
$scope.test = 'test';
dataService.getData().then(function(data) {
$scope.dataset = data.reduce(function (obj, item) {
obj[item.seatId.trim()] = item;
item.fullName = item.fName + ' ' + item.lName;
return obj;
}, {});
});
});
angular.module('FFPA').service('dataService', function($http){
this.getData = function(){
return $http.get("data.json").then(
function(response){
return response.data;
}, function() {
return {err:"could not get data"};
}
);
}
});
//directive loads SVG into DOM
angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
return {
restrict: 'A',
templateUrl: 'test.svg',
scope: {
'dataset': '=svgFloorplan'
},
link: {
pre: function (scope, element, attrs) {
var groups = element[0].querySelectorAll("g[id^='f3']");
scope.changeName = function (groupId) {
if (scope.dataset[groupId] && scope.dataset[groupId].lastName.indexOf('changed') === -1) {
scope.dataset[groupId].lastName += ' changed';
}
}
groups.forEach(function(group) {
var groupId = group.getAttribute('id');
if (groupId) {
var datasetBinding = "dataset['" + groupId + "']";
group.setAttribute('svg-floorplan-popover', datasetBinding);
$compile(group)(scope);
}
});
}
}
}
}]);
angular.module('FFPA').directive('svgFloorplanPopover', ['$compile', function ($compile) {
return {
restrict: 'A',
scope: {
'person': '=svgFloorplanPopover'
},
link: function (scope, element, attrs) {
scope.changeName = function () {
if (scope.person && scope.person.fullName.indexOf('changed') === -1) {
scope.person.fullName += ' changed';
}
}
scope.htmlPopover = 'popoverTemplate.html';
element[0].setAttribute('uib-popover-template', "htmlPopover");
element[0].setAttribute('popover-append-to-body', 'true');
element[0].setAttribute('popover-trigger', "'outsideClick'");
element[0].querySelector('text').textContent += '{{ person.fullName }}';
element[0].removeAttribute('svg-floorplan-popover');
$compile(element)(scope);
}
}
}]);
你的 HTML body 现在看起来像:
<body style="background-color:#5A8BC8;">
<div ng-app="FFPA" ng-controller="myCtrl">
<div svg-floorplan="dataset"></div>
</div>
</body>
HTML 用于弹出窗口:
<div><button type="button" class="btn btn-default" ng-click="changeName()">{{ person.fullName }}</button></div>
这是工作中的 plunker: http://plnkr.co/edit/uHgnZ1ZprZRDvL0uIkcH?p=preview