How to pass the fetched node data from directive to controller - javascript

topology.controller.js
define(['app/topology/topology.module','app/topology/topology.services', 'app/topology/topology.directives'], function(topology, service) {
topology.controller('TopologyCtrl', ['$scope', '$rootScope', '$location', 'NetworkTopologySvc' , function ($scope, $rootScope, $location, NetworkTopologySvc) {
$rootScope['section_logo'] = 'assets/images/logo_topology.gif';
var graphRenderer = null;
$scope.createTopology = function() {
NetworkTopologySvc.getNode("flow:1", function(data) {
$scope.topologyData = data;
});
};
$scope.createTopology();
}]);
});
topology.directives.js
define(['app/topology/topology.module', 'vis'], function(topology, vis) {
topology.directive('topologySimple', function() {
// constants
var width = 800,
height = 800;
return {
restrict: 'E',
scope: {
topologyData: '='
},
link: function($scope, iElm, iAttrs, controller, $window, $location) {
$scope.$watch('topologyData', function (ntdata) {
if(ntdata){
// visinit(inNodes, inEdges, container, inOptions) {
var inNodes = $scope.topologyData.nodes;
var inEdges = $scope.topologyData.links;
var container = iElm[0];
// legend moved to topology controller
var data = {
nodes: inNodes,
edges: inEdges
};
var color = '#66FFFF',
hl = '#0066FF',
hover = '#33CC33',
BLACK = '#2B1B17';
var options =
{
width: '100%',
height: '750px',
nodes: {
widthMin: 20,
widthMax: 64,
fontColor: BLACK
},
edges: {
length: 80,
color: {
color: '#070707',
highlight: hl,
hover: hover
}
},
physics: {
barnesHut: {
gravitationalConstant: -7025
}
},
hover: true,
groups: {
'switch': {
shape: 'image',
image: 'assets/images/Device_switch_3062_unknown_64.png'
},
'host': {
shape: 'image',
image: 'assets/images/Device_pc_3045_default_64.png'
}
},
keyboard:true,
tooltip: {
delay: 300,
fontColor: "black",
fontSize: 14, // px
fontFace: "verdana",
color: {
border: "#666",
background: "#FFFFC6"
}
}
};
var graph = new vis.Graph(container, data, options);
return graph.on("click", function (params) {
console.log(params);
});
}
});
}
};
});
});
How to send the data received into 'params' to controller.
The params contains :
"nodes": [1,2],
"links": [3,4]
Please help me with this.
Any help would be appreciated.

You have several ways to do so.
using service or factory, but you can also pass data using $rootScope - not a good practice. simple $rootScope example:
in directive:
$rootScope.nodes = nodes;
and you can simple access them at your controller:

Related

SpacingZoom Directive - GoJS

Here is my Plunker
The above Plunker is a basic example for connecting the nodes followed by the Links.
I would like to update my Plunker directive in order to add the SpacingZoom. Here is the example
The example is given for SpacingZoom in GoJS official website using javascript. I want that to be converted into an angular directive.
Here is my code
scripts.js
var app = angular.module('app', []);
app.directive('goDiagram', function($http) {
return {
restrict: 'E',
template: '<div></div>',
replace: true,
scope: {
model: '=goModel'
},
link: function(scope, element, attrs) {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;
var rainbow = $(go.Brush, "Linear", {
0: "red",
1: "green"
});
var diagram = $(go.Diagram, element[0], {
nodeTemplate: $(go.Node, "Auto", {
locationSpot: go.Spot.Center
}, {
width: 120,
height: 15,
locationSpot: go.Spot.Center
},
new go.Binding("location"),
$(go.Shape, {
fill: "#e74c3c",
stroke: '#c0392b'
}, {
portId: "",
cursor: "pointer",
strokeWidth: 0,
}),
$(go.TextBlock, {
margin: 0,
stroke: "#eee"
},
new go.Binding("text", "key")
)
),
linkTemplate: $(go.Link, {
// routing: go.Link.AvoidsNodes,
reshapable: true,
resegmentable: true
},
$(go.Shape,
{ strokeWidth: 3 , stroke: rainbow },
// new go.Binding("stroke", rainbow),
),
$(go.Shape, {
toArrow: "Standard"
}),
),
});
function updateAngular(e) {
if (e.isTransactionFinished) {
scope.$apply();
}
}
function updateSelection(e) {
diagram.model.selectedNodeData = null;
var it = diagram.selection.iterator;
while (it.next()) {
var selnode = it.value;
// ignore a selected link or a deleted node
if (selnode instanceof go.Node && selnode.data !== null) {
diagram.model.selectedNodeData = selnode.data;
break;
}
}
scope.$apply();
}
// watch scope
scope.$watch("model", function(newmodel) {
if (newmodel != undefined) {
var oldmodel = diagram.model;
if (oldmodel !== newmodel) {
diagram.removeDiagramListener("ChangedSelection", updateSelection);
diagram.model = newmodel;
diagram.addDiagramListener("ChangedSelection", updateSelection);
}
}
});
}
}
});
app.controller('appController', function($scope) {
$scope.init = function(d) {
$scope.hello = "Hello Plunker!!!!";
$scope.model = new go.GraphLinksModel(
[{
key: "Alpha",
color: "lightblue",
location: new go.Point(150, 130)
}, {
key: "Beta",
color: "orange",
location: new go.Point(350, 180)
}, {
key: "Gamma",
color: "lightgreen",
location: new go.Point(150, 230)
}, {
key: "Delta",
color: "pink"
}], [{
from: "Alpha",
to: "Beta",
inColor: "red",
outColor: "blue"
}, {
from: "Alpha",
to: "Gamma",
inColor: "yellow",
outColor: "blue"
}, {
from: "Beta",
to: "Gamma",
inColor: "green",
outColor: "pink"
}, {
from: "Gamma",
to: "Delta",
inColor: "black",
outColor: "red"
}, {
from: "Delta",
to: "Alpha",
inColor: "violet",
outColor: "green"
}]);
$scope.model.selectedNodeData = null;
}
});
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.5.10" data-semver="1.5.10" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.min.js"></script>
<script data-require="angular-route#1.5.8" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular-route.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://gojs.net/latest/release/go.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="appController">
<div ng-init="init()">
<h1>{{hello}}</h1>
<go-diagram go-model="model" style="border: solid 1px black; width:100%; height:400px"></go-diagram>
</div>
</body>
</html>
Need one more solution, From the connected chart you can see the Links from Delta to Gamma, the line is drawing above the Alpha Node. I don't want to the lines to be drawn any of the nodes.

AngularJS: How do I get a directive to update when the data changes?

I am trying to get a directive to update when the data changes in my controller... currently I have gotten the ng-repeat to update some of the values but the directive inside the repeat does not appear to get called again.
I have a directive like so:
app.directive("donut", function () {
return {
restrict: 'E',
scope: {
donut: '='
},
replace: true,
link: link
};
function link($scope, $elem, $attr) {
var chart = {
//chart functionality
}
$scope.$watch($scope.donut,function(){chart.createChart($elem, $scope.donut)});
}
}
I have this in my html:
<donut class="donut-holder-inventory" donut="data.donutData"></donut>
And then I have this in the controller:
$scope.regions = [
{
region: 'Team Number One',
data: [
{
title: 'Number One',
subtitle: 'Quantity Available',
daysSupply: 0,
targetNumber: 108,
actualNumber: 46,
targetPercentage: 1,
actualPercentage: 0,
color: 'red',
donutData: {
name: 'Number One',
fullscale: false,
fullsection: 108,
values: [46],
labeltext: ['% to goal'],
labelunits: "percent",
color: ["#f44336"],
labels: false,
height: 150
}
}
}
]
Then I have this timeout in the controller just to test the data binding:
function timerFunc(dat) {
$scope.$apply(function () {
console.log("changing")
var dat = $scope.regions;
for (var x = 0; x < dat.length; x++) {
var cdat = dat[x].data;
for (var c = 0; c < cdat.length; c++) {
console.log(cdat[c].targetNumber)
var nval = Math.round($scope.regions[x].data[c].actualNumber + (Math.random() * 1000))
$scope.regions[x].data[c].targetNumber = nval;
$scope.regions[x].data[c].donutData.fullsection = nval;
}
}
});
};
var timer = setInterval(function () {
timerFunc()
}, 1000);
The donut directive is bound to the .donutData property, the rest of the view updates but the donut directive does not, what do I need to do to make the directive run its link function again?
Call $watch with true as the third argument:
$scope.$watch("donut", function(){chart.createChart($elem, $scope.donut)}, true);
By default when comparing two complex objects in JavaScript, they will be checked for "reference" equality, which asks if the two objects refer to the same thing, rather than "value" equality, which checks if the values of all the properties of those objects are equal.

Angularjs - Calling a controller function inside directive

I'm trying to use jQuery Sparkline charts with Angularjs. I have multiple charts to display, so I've decided to create a function in the controller and call it for each chart(directive).
JS
controller
.controller('sparklineCtrl', [function(){
this.sparklineBar = function(id, values, height, barWidth, barColor, barSpacing) {
$('.'+id).sparkline(values, {
type: 'bar',
height: height,
barWidth: barWidth,
barColor: barColor,
barSpacing: barSpacing
})
}
}])
directive
.directive('sparklineBar', function(){
return {
restrict: 'A',
scope: {
slBar: '&'
},
link: function(scope, element) {
scope.slBar('stats-bar', [6,4,8,6,5,6,7,8,3,5,9,5,8,4,3,6,8], '45px', 3, '#fff', 2);
}
}
})
HTML
<div data-ng-controller="sparklineCtrl as spctrl">
<div class="chart" id="stats-bar" data-sparkline-bar data-sl-bar="spctrl.sparklineBar()"></div>
</div>
Running the above code there is no error in browser console but it's not rendering the chart at all. I don't know what is wrong in my code. When I try to place function's code directly inside directive, it's working.
.directive('sparklineBar', function(){
return {
restrict: 'A',
link: function(scope, element) {
$('#stats-bar').sparkline([6,4,8,6,5,6,7,8,3,5,9,5,8,4,3,6,8], {
type: 'bar',
height: 45,
barWidth: 3,
barColor: '#fff',
barSpacing: 2
})
}
}
})
I don't want to use the above way as I need multiple charts. Please help me fix this using controller functions.
It is better to move the function logic into service/factory then using Injection to be used in your directive.
Example:
app.factory('sparkService', function () {
var ss = {} ;
ss.slBar= function(id, values, height, barWidth, barColor, barSpacing) {
$('.'+id).sparkline(values, {
type: 'bar',
height: height,
barWidth: barWidth,
barColor: barColor,
barSpacing: barSpacing
});
};
return ss;
}
While in directive
.directive('sparklineBar', ['sparkService',function(sparkService){
return {
restrict: 'A',
scope: {
slBar: '&'
},
link: function(scope, element) {
sparkService.slBar('stats-bar', [6,4,8,6,5,6,7,8,3,5,9,5,8,4,3,6,8], '45px', 3, '#fff', 2);
}
}]);

Reload angular directive to redraw morris.js charts after the API call

I'm using Morris charts with angular to show graphical reports in which the data comes from our backend server via an rest API call.
I'm able to see the retrieved data in console log but it's not displaying in charts. I found that the directive barchart is getting loaded before the api call and hence displaying data available in $scope.myModel.
I'm trying to find if there is some way in angular which can help me to reload the directive when data is received from api call. Could someone help me with this?
Bar Chart generated from code:
Here's my code
var sampleApp = angular.module('sample',[]);
sampleApp.directive('barchart', function() {
return {
// required to make it work as an element
restrict: 'E',
template: '<div></div>',
replace: true,
// observe and manipulate the DOM
link: function($scope, element, attrs) {
var data = $scope[attrs.data],
xkey = $scope[attrs.xkey],
ykeys= $scope[attrs.ykeys],
labels= $scope[attrs.labels];
Morris.Bar({
element: element,
data: data,
xkey: xkey,
ykeys: ykeys,
labels: labels
});
}
};
});
sampleApp.controller('sampleController',function($scope, $http){
$scope.values = []
$scope.xkey = 'range';
$scope.ykeys = ['total_tasks', 'total_overdue'];
$scope.labels = ['Total Tasks', 'Out of Budget Tasks'];
$http.get('http://api.*******.com/api/getAppID/?parameter=whatsapp').success( function(res) {
if(!res.error) {
if(res.status==1) res.status=true
else res.status=false
$scope.values[0] = res.metrices.total_shares
$scope.values[1] = res.metrices.unique_share_count
$scope.values[2] = res.metrices.total_clicks
$scope.values[3] = res.metrices.total_downloads
}
})
$scope.myModel = [
{ range: 'January', total_tasks: $scope.values[0], total_overdue: 5 },
{ range: 'January', total_tasks: $scope.values[1], total_overdue: 8 },
{ range: 'January', total_tasks: $scope.values[2], total_overdue: 1 },
{ range: 'January', total_tasks: $scope.values[3], total_overdue: 6 }
];
});
HTML PART:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://cdn.oesmith.co.uk/morris-0.4.3.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="http://cdn.oesmith.co.uk/morris-0.4.3.min.js"></script>
<script src="js/sample.js"></script>
<meta charset=utf-8 />
</head>
<body ng-app="sample" ng-controller="sampleController">
<barchart xkey="xkey" ykeys="ykeys" labels="labels" data="myModel"></barchart>
</body>
You don't need the 'flag' variable, you need to watch 'myModel' instead and you don't need to create a new Morris chart everytime model changes, second time model changes you just need to call the morris setData method.
I started my own directive starting from yours and I ended up with this code which works fine and redraws chart on window resize event, maybe someone could use it.
(function () {
'use strict';
var module = angular.module('app.charts', []);
module.directive('areachart', function ($window) {
return {
restrict: 'E',
template: '<div></div>',
replace: true,
link: function ($scope, element, attrs) {
var morris;
angular.element($window).bind('resize', function () {
if (morris) {
console.log('morris resized');
morris.redraw();
}
});
attrs.$observe('value', function (val) {
if (!morris) {
console.log('creating chart');
morris = Morris.Area({
element: element,
data: angular.fromJson(val),
xkey: $scope[attrs.xkey],
ykeys: $scope[attrs.ykeys],
labels: $scope[attrs.labels]
});
} else {
console.log('setting chart values');
morris.setData(angular.fromJson(val));
}
});
}
};
});
}).call(this);
HTML
<areachart xkey="xkey" ykeys="ykeys" labels="labels" data-value="{{myModel}}"></areachart>
In your controller:
$scope.xkey = 'y';
$scope.ykeys = ['a', 'b'];
$scope.labels = ['Series A', 'Series B'];
$scope.myModel = [
{ y: '2006', a: 100, b: 90 },
{ y: '2007', a: 75, b: 65 },
{ y: '2008', a: 50, b: 40 },
{ y: '2009', a: 75, b: 65 },
{ y: '2010', a: 50, b: 40 },
{ y: '2011', a: 75, b: 65 },
{ y: '2012', a: 100, b: parseInt((Math.random() * 10000) / 10) }
I have tried really hard on this and managed to solve it. Posting this answer to help others fellow coders.
var sampleApp = angular.module('sample',[]);
sampleApp.directive('barchart', function() {
return {
// required to make it work as an element
restrict: 'E',
template: '<div></div>',
replace: true,
// observe and manipulate the DOM
link: function($scope, element, attrs) {
$scope.$watch('flag', function() {
$scope.myModel = [
{ range: 'January', total_tasks: $scope.values[0], total_overdue: 5 },
{ range: 'January', total_tasks: $scope.values[1], total_overdue: 8 },
{ range: 'January', total_tasks: $scope.values[2], total_overdue: 1 },
{ range: 'January', total_tasks: $scope.values[3], total_overdue: 6 }
];
console.log($scope.flag + $scope.values+' The one we want watch')
$scope.xkey = 'range';
$scope.ykeys = ['total_tasks', 'total_overdue'];
$scope.labels = ['Total Tasks', 'Out of Budget Tasks'];
var data = $scope[attrs.data],
xkey = $scope[attrs.xkey],
ykeys= $scope[attrs.ykeys],
labels= $scope[attrs.labels];
var setData = function(){
console.log('inside setData function');
Morris.Bar({
element: element,
data: data,
xkey: xkey,
ykeys: ykeys,
labels: labels
});
};
if ($scope.flag == 1) {
attrs.$observe('data',setData)
}
});
}
};
});
sampleApp.controller('sampleController',function($scope, $http){
$scope.flag = 0;
$scope.values = [];
$http.get('http://api.*******/api/*****/?appname=whatsapp').success( function(res) {
if(!res.error) {
if(res.status==1) res.status=true
else res.status=false
$scope.values[0] = res.metrices.total_shares
$scope.values[1] = res.metrices.unique_share_count
$scope.values[2] = res.metrices.total_clicks
$scope.values[3] = res.metrices.total_downloads
$scope.flag = 1;
console.log($scope.flag+"in api call"+$scope.values)
}
})
});
This worked for me.
But if we remove that (flag==1) condition in $watch it redraws the chart 2 times with overlapping. I would appreciate if anyone can improve this answer.
This link was really helpful.
[1]:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest
The only thing needed to "redraw" is to call $apply... Which will call $digest... which will check if any of the $watchers have(or not) changed, if so re painting/drawing the object.
setTimeout(function() {
$scope.$apply(function (){
morris = Morris.Area({
element: element,
data: angular.fromJson(val),
xkey: $scope[attrs.xkey],
ykeys: $scope[attrs.ykeys],
labels: $scope[attrs.labels]
});
});
},1500);

How to load a directive template before evaluating javascript

I'm using KineticJS in conjunction with AngularJS and I want to be able to stash the necessary javascript into a directive. All of the examples I've seen for KineticJS wrap the required logic in a script tag using the defer attribute, i.e.
<script defer="defer">
window.onload = function() { ... }
</script>
Is it possible to load the template of the directive while deferring the execution of any business logic (in an equivalent way)? I've tried using the controller, link, compile functions to no avail. Here's an example:
'use strict';
angular.module('app')
.directive('canvasInitializer', ['$window', function ($window) {
return {
template: "<div id='canvas-container'></div>", // load this!
restrict: 'C',
compile: function (element, attrs) {
return {
post: function(scope, element, attrs) {
// defer this?
$window.onload = function () {
var stage = new Kinetic.Stage({
container: 'canvas-container',
width: 800,
height: 600
});
var layer = new Kinetic.Layer();
var blueRect = new Kinetic.Rect({
x: 50,
y: 75,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
});
layer.add(blueRect);
stage.add(layer);
}
}
}
}
}
}]);
Aaaaaaand suddenly it looks like I found the answer to my problem. Wrapping the logic in $timeout(function () { ... }) did the trick. Notice that I didn't even provide a specific time for $timeout.
'use strict';
angular.module('app')
.directive('canvasInitializer', ['$timeout', function ($timeout) {
return {
template: "<div id='canvas-container'></div>"
restrict: 'C',
link: function (scope, element, attrs) {
$timeout(function () {
var stage = new Kinetic.Stage({
container: 'canvas-container',
width: 800,
height: 600
});
var layer = new Kinetic.Layer();
var blueRect = new Kinetic.Rect({
x: 50,
y: 75,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
});
layer.add(blueRect);
stage.add(layer);
});
}
}
}]);

Categories

Resources