访问$ scope从AngularJS中的指令(Accessing $scope From a Directive in AngularJS)

编程入门 行业动态 更新时间:2024-10-26 17:28:19
访问$ scope从AngularJS中的指令(Accessing $scope From a Directive in AngularJS)

由于我们缺乏与AngularJS一起开发的专业知识,我们在开发过程中遇到了另一个障碍。

我们正在开发一个Angular / Web API应用程序,其中我们的页面仅包含一个交互式SVG图表,当用户将鼠标悬停在Angular指令中的特定SVG标记上时,该图表显示数据。

目前在应用程序中有两个自定义指令。

指令一 - 将SVG文件加载到网页中 指令二 - 添加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>

指令2从与每个SVG标记ID相对应的注入服务中加载JSON数据。

//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标签和角度范围变量来创建工具顶端,用户悬停在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:

POC Plunk

也正在考虑为toolips使用Angular Bootstrap UI:

Bootstrap UI Plunk

希望这是有道理的。

Due to our lack of expertise in developing with AngularJS, we've come to another roadblock in our development process.

We are developing a Angular/Web API application where our page only consists of an interactive SVG diagram that displays data when a user hovers over a particular SVG tag in an Angular directive.

There are currently two custom directives in the application.

Directive One - Loads SVG file into web page Directive Two - Adds SVG element hover event/data filter

DIRECTIVE ONE:

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

The SVG diagram contains tags with unique identifiers, ie:

<g id="f3s362c12"></g>

Directive Two loads JSON data from an injected service that corresponds to each of the SVG tag id's.

//filters json based on hover item dataService.getData().then(function(data) { thisData = data.filter(function (d) { return d.seatId.trim() === groupId });

As shown above, Directive Two also adds a hover event function that filters the JSON data based on the tag that was hovered over.

IE: If a user hovers over , a filter in the directive would return this JSON record:

{"Id":1, "empNum":null, "fName":" Bun E.", "lName":"Carlos", ... "seatId":"f3s362c12 ", "floor":3, "section":"313 ", "seat":"12 "}

DIRECTIVE TWO:

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

The problem we now have is now (besides having minimal Angular experience and facing a unique and difficult implementation problem) is that we're trying to implement a way to create a tooltop using a div tag and an angular scope variable that we can display when a user hovers over the SVG tag element (instead of a Javascript alert which is demonstrated in the Plunker POC link below).

Since the data is being driven by the directive and the directive is already taking "cubehvr" as a parameter:

angular.module("FFPA").directive('*cubehvr*', ['$compile', 'dataService', function ($compile, dataService)

We're stuck since we don't know how to set an HTML page scope directive or variable, say like this from our second directive:

<div uib-popover="Last Name: {{empData.lName}}" popover-trigger="'mouseenter'" type="div" class="btn btn-default">Tooltip </div>

Or as simple as say, this:

<div emp-info></div>

The div tooltips will have html buttons that call Web API Update functionality.

We have a scaled down POC Plunk here:

POC Plunk

Also were thinking about using the Angular Bootstrap UI for the toolips:

Bootstrap UI Plunk

Hope that makes sense.

最满意答案

//编辑。 我再次读你的问题,并通过我的答案。 我没有完全回答你的问题,因为它是多层次的。 现在我将通过你的所有关切并尝试回答它们:

将$ scope传递给其他指令。

$ scope是MVVM设计模式中的Model-View,它将您的模板(视图)和您的模型粘合在一起。 理论上你可以将$ scope传递给另一个指令,但我认为这是一种反模式。

指令之间的通信。至少有4种方法可以用来传达指令:

分享相同的范围,你几乎在你的plunker中做了什么,只是没有在你的指令规范中定义任何'范围'。 我不确定这是否是最好的方式,因为您的任何指令都可能会影响示波器的数据。 创建独立的作用域并使用ng-model或$ watch,这是更安全的方法,但它需要更多的开销。 在这种情况下,您将该变量向下传递,这是您的范围。 这是双向绑定。 你可以推和拉值。 $腕表 创建一个服务,在其中为事件总线或变量存储一些东西 您可以将您的指令与事件进行通信: $ on $ emit这对分层指令很有效(如此有效,您将不得不创建独立的子范围)

向SVG的孩子添加弹出窗口。 Bootstrap能够将弹窗添加到body而不是父元素。 这对于SVG很有用: https ://angular-ui.github.io/bootstrap/#!#popover

我重构了您的代码以使用两个指令,并将数据加载到控制器中。 一个指令包装弹出窗口,第二个指令传递数据,弹出窗口现在使用模板,所以它正在使用角度编译:

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

//Edit. I read your question once again and go thru my answer. I didn't fully answer your question as it's multi-layered. Now I'll go thru all your concerns and try to answer them:

Passing $scope to the other directives.

$scope is Model-View in MVVM design pattern, it glues your Template (View) and your Model together. In theory you could probably pass the $scope to the other directive, but I think it's an anti-pattern.

Communication between directives.There are at least 4 methods which you can use to communicate your directives:

Share the same scope, what you almost did in your plunker, just don't define any 'scope' in your directives' spec. I'm not sure if it's the best way to go with as any of your directives can malform your scope's data. Create isolated scopes and use ng-model or $watch, it's safer method, but it needs more overhead. In such case you pass the variable down, which you scope.$watch. It's two-way-binding. You can push and pull the value. $watch Create a service, where you keep something like an event-bus or a storage for your variables You can communicate your directives with events: $on $emit That works well with hierarchical directives (so effectively, you would have to create isolated child scopes)

Add popover to a SVG's child. Bootstrap has an ability to add popover to body instead of to parent element. It's useful for SVGs: https://angular-ui.github.io/bootstrap/#!#popover

I refactored your code to use two directives, and the data is loaded in controller. One directive wraps the popover and the second one passes the data, also the popover uses template now, so it's being compiled with 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); } } }]);

And your HTML body now looks like:

<body style="background-color:#5A8BC8;"> <div ng-app="FFPA" ng-controller="myCtrl"> <div svg-floorplan="dataset"></div> </div> </body>

HTML for popover:

<div><button type="button" class="btn btn-default" ng-click="changeName()">{{ person.fullName }}</button></div>

Here is working plunker: http://plnkr.co/edit/uHgnZ1ZprZRDvL0uIkcH?p=preview

更多推荐

本文发布于:2023-07-14 23:43:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1108366.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:指令   AngularJS   scope   Accessing   Directive

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!