由于我们在开发AngularJS方面缺乏专业知识,我们在开发过程中遇到了另一个障碍。
我们正在开发一个Angular / Web API应用程序,其中我们的页面只包含一个交互式SVG图表,当用户将鼠标悬停在Angular指令中的特定SVG标记上时,该图表显示数据。
目前应用程序中有两个自定义指令。
指令一:
//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>
指令二从注入的服务加载JSON数据,该服务对应于每个SVG标记ID。
//filters json based on hover item
dataService.getData().then(function(data) {
thisData = data.filter(function (d) {
return d.seatId.trim() === groupId
});
如上所示,Directive Two还添加了一个悬停事件函数,该函数根据悬停的标记过滤JSON数据。
IE:如果用户将鼠标悬停,则指令中的过滤器将返回此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标签和角度范围变量来实现创建tooltop的方法可以在用户将鼠标悬停在SVG标记元素上时显示(而不是在下面的Plunker POC链接中演示的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工具提示将具有调用Web API更新功能的html按钮。
我们在这里缩小了POC Plunk:
还考虑使用Angular Bootstrap UI作为工具:
希望这是有道理的。
答案 0 :(得分:1)
//编辑。我再次阅读你的问题并通过我的回答。我没有完全回答你的问题,因为它是多层次的。现在我将通过你所有的担忧并尝试回答它们:
$ scope是MVVM设计模式中的Model-View,它将您的模板(View)和模型粘合在一起。从理论上讲,你可能会将$ scope传递给另一个指令,但我认为这是一种反模式。
指令之间的通信。您可以使用至少4种方法来传达指令:
将弹片添加到SVG的孩子。 Bootstrap能够将popover添加到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 style="background-color:#5A8BC8;">
<div ng-app="FFPA" ng-controller="myCtrl">
<div svg-floorplan="dataset"></div>
</div>
</body>
popover的HTML:
<div><button type="button" class="btn btn-default" ng-click="changeName()">{{ person.fullName }}</button></div>
这是工作的plunker: http://plnkr.co/edit/uHgnZ1ZprZRDvL0uIkcH?p=preview