Angular & D3.js: Why is element[0] undefined? - javascript

I am working on an angular application and just starting to work with D3.js.
I am following along on a tutorial to create custom directives.
<my-chart></my-chart>
I am doing this:
...
.directive('myChart', function () {
function link() {
var svg = d3.select(element[0]).append('svg');
...
return {
'link': link,
'restrict': 'E'
}
})
...
But I am always getting back ReferenceError: element is not defined
If I give my directive an id, then everything works:
...
var svg = d3.select(document.getElementById('myChart')).append('svg');
...
I am not sure why as since I am able to talk to the element when it has an id, I don't think it's a timing problem. But, I don't know what else it could be. Any suggestions are greatly appreciated!

Currently you haven't specified element in link function, and you were looking for element which is undefined. You should have element in your link function to get access to angular compiled DOM.
function link(scope, element, attrs) {

Related

Creating dynamic link with ui-sref inserting with highcharts

I'm trying to create a category xAxis that acts as links to other areas of the app. I want to avoid using href because that will reload everything. I have the core of the problem down, I think.
labels: {
formatter: function () {
let newvar = $compile(`<a class="link">${this.value} - ${vm.tableData.contentMain[this.pos].bothNeeded} - ${vm.tableData.contentMain[this.pos].percentageCompleted}</a> - `)($scope)
return angular.element(newvar[0]);
},
useHTML: true
}
My problem is that [object Object] shows on the page instead of the link. I believe I need to compile in order to get the ui-sref to work, which is why it is there. Any help creating these dynamic links greatly appreciated!
I am running Angular 1.6 in case that matters
UPDATE
Hopefully I am misunderstanding how $compile works. Here is a bad, non-working example OUTSIDE of highcharts
http://jsfiddle.net/HB7LU/31241/
From HighCharts Docs formatter returns String. So you have nothing to do with it. $compile generated DOM element that has been regestered to digest cycle. So its not out case.
However you can generate string something like:
formatter: function () {
return '<div style="width:70px" onclick="alert(\''+this.value+'\')">'+$scope.someValues[this.value]+'</div>';
},
Demo Fiddle
I want to avoid using href ...
you can build custom href by modifieng it based on this.vaue
As a side note:
When you print in DOM $compiled object, you get something like: {"0":{"ng-1505336047666":6},"length":1} where 0 is id of $scope and 1505336047666 element id that regenerates each build
Okay so if you provide a cloneAttachFn and then assign the result to the scope you can get it to do what you're looking for.
HTML
<div ng-controller="MyCtrl">
Hello, {{name}}!
{{newthing}}
</div>
Javascript
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope, $compile) {
$scope.name = 'Superhero';
$scope.link = $compile(`<div>Test</div>`)(
$scope,
function(clonedElement, scope) {
console.log(clonedElement[0]);
$scope.newthing = clonedElement[0].outerText;
}
);
}
Here's the fiddle: http://jsfiddle.net/HB7LU/31285/

Override Angular Directive link

I have limited knowledge of Angular so please bear with me. I am in a situation where I can only modify one js file which is included BEFORE all of the Angular stuff. There is a directive that is causing a problem, yet I can not modify it directly. So I've tried to override it by adding the snippet below in a document ready block:
app.directive('selectionChange', function($rootScope){
return {
priority: 1,
terminal: true,
link: function(scope, el, attr) {
console.log('works');
};
};
});
I can see this directive added to the end of the invokeQueue, but it is never executed. How do I get this attached? Thanks!
UPDATE:
Sorry, let me try to clarify. Problem is, the original directive continues to fire, but the newly attached one does not (tested by using console.log and alert). The markup is something like this:
<html>
<head>
...
<script src="[the file I can modify].js"></script>
...
</head>
<body>
...
<script src="angular.js"></script>
<script src="directives.js"></script> // here is where the existing selectionChange directive is defined
...
</body>
</html>
Here's a plunker
$(function () {
var app = angular.module('app');
app.config(function ($provide) {
$provide.decorator('badDirective', function ($delegate) {
var badDirective = $delegate[0];
var link = function (scope, element) {
element.text('good');
}
var originalCompile = badDirective.compile || function () {};
badDirective.compile = function () {
originalCompile.apply(badDirective, arguments);
// compile returns link fn, directive 'link' property will be ignored anyway
return link;
}
return $delegate;
});
});
})
Doing it on 'ready' state (e.g. jQuery ready implementation) is the right thing. This way the code will be launched before the bootstrapping process (it will be queued on 'ready' via ng-app as soon as angular.js is loaded).
bad directive is just badDirective service internally which contains an array of DDO (because there can be several directives with the same name). And it can be decorated, as any other service.
Link function can be defined with either link or compile (it can return link) DDO properties. The second overrides the first, so always stick to compile when decorating directives.
In the file you can modify create a script tag with a (reference/definition) to your new directive and place that tag at the bottom of the body right after the troubled directive definition. By being the last one defined you'll ensure that is your directive the one rendered.

AngularJS post process links

I have a laravel app that spits out the default pagination, however it adds "page" to every link and ignores what is currently in the query string. This is not a laravel question though just want to explain the issue.
Basically I need to add some sort of handler for the pagination links. The rest of the query string is handled via controls I made by creating a directive, so they are easily accessible. Through the controller I can easily create the "proper" url however I want to figure out the best way to do it.
I hear a lot that doing anything with the DOM itself is best avoided in angularJS because its not jQuery, so what is the best practice way to handle this issue in angularjs?
in jQuery I would do this.
jQuery('.pagination-wrap a').on('click', function(event) {
event.preventDefault();
var link = generateProperLink(jQuery(this).attr('href'));
window.location.href = link;
});
But how would I do that in an angular controller (minus the obvious use jQuery).
Nothing wrong with using jQuery in AngularJS, you just need to let AngularJS know if it needs to do anything. That could be as simple as a $scope.$apply() in the callback of jQuery event handler (or any non-angular construct), or something more complex, like compiling DOM changes in the context of a scope.
In your case, does angular need to know the "proper link"? If not, you could wrap your code in an attribute directive.
The following is illustrative, I haven't tested it
(function(app, $) {
app.directive('fixUrl', fixUrl);
function fixUrl() {
return {
restrict: 'A',
link: link
};
function link(scope, element, attributes) {
$(element).on('click', function(event) {
event.preventDefault();
var link = generateProperLink(element);
window.location.href = link;
});
}
function generateProperLink(element){
return $(element).attr('href');
}
}
}(angular.module('app'), jQuery));
Then...
<a class='pagination' fix-url>Whatever</a>
I think you can use angular.element jqlite which is subset of jquery and it has most of the jquery methods u can use , refer https://docs.angularjs.org/api/ng/function/angular.element
You can use directive for the reusable components
<a id="someID" urlUsage><a>
//directive
App.directive('urlUsage',function(){
var generateProperLink = function(ele){
return ele.attr('href');
}
return {
restrict: 'A',
link:function(scope,ele,attrs) {
ele.on('click',function(){
event.preventDefault();
var link = generateProperLink(ele);
window.location.href = link;
})
}
}
})

How to call a non-jquery object method inside an angularjs directive?

I'm using Polymer and AngularJS together, and I want the page to show a toast after a successful form submitting.
Here's my HTML:
<div ng-view></div>
<paper-toast id="toast" text="{{$rootScope.message}}" show-toast></paper-toast>
My $routeProvider has indicated different templates and controllers for different routes, so I put the toast element outside. Since the submitting causes a page jump, I need to put the message where the toast element can access, i.e. $rootScope.
And JS:
app.directive('showToast', ['$rootScope', function($rootScope) {
restrict: 'A',
link: function($scope, element, attrs) {
var toast = document.querySelector('#toast');
if ($rootScope.message != '') {
toast.show();
toast.addEventListener('core-overlay-close-completed', function() {
$rootScope.message = '';
})
}
}
}])
But an error occurs saying that undefined is not a function at link, pointing to toast.show(). But if I put the content of link function into that page controller, it can work.
What's wrong with directive? And as you can see, the link function has a parameter element, but since the show() method is not a jQuery method, I don't know how to call it through element.
Remove this line
var toast = document.querySelector('#toast');
Because the toast element should be a global variable on the window object (you could access it by using window.toast if there is some other scope variable interfering with it)
My apologies. I made a mistake. the element parameter is an array of DOM nodes, so I can just call show() method by element[0].show(). Sorry for confusing if any.

Directive at angularjs and custom method/html

I have this code:
<body ng-controller="testController">
<div test-directive transform="transform()">
</div>
<script type="text/ng-template" id="testDirective.html">
<div>
<p>
{{transform()}}
</p>
</div>
</script>
<script>
angular.module("Test", [])
.directive("testDirective", function() {
return {
templateUrl: "testDirective.html",
scope: {
transform: "&"
},
link: function(scope) {
}
};
})
.controller("testController", function($scope) {
$scope.transform = function() {
return "<a ng-click='somethingInController()'>Do Something</a>";
};
$scope.somethingInController = function() {
alert("Good!");
};
});
</script>
</body>
So basically what I want to accomplish is to create a directive with a method that will be called from the controller. And that method will do something with the values passed (in this example it does not receives nothing, but in the real code it does).
Up to that point is working. However, the next thing I want to do is create an element that will call a method in the controller. The directive does not knows what kind of element will be (can be anything) nor what method will be. Is there any way to do it?
Fiddle Example:
http://jsfiddle.net/abrahamsustaita/C57Ft/0/ - Version 0
http://jsfiddle.net/abrahamsustaita/C57Ft/1/ - Version 1
FIDDLE EXAMPLE WORKING
http://jsfiddle.net/abrahamsustaita/C57Ft/2/ - Version 2
The version 2 is now working (I'm not sure if this is the way to go, but it works...). However, I cannot execute the method in the parent controller.
Yes. However there is a few problems with your code. I will start by answering your question.
<test-directive transform='mycustommethod'></test-directive>
// transform in the directive scope will point to mycustommethod
angular.module('app').directive('testDirective', function() {
return {
restrict: 'E',
scope: {
transform: '&'
}
}
});
The problem is that printing the html will be escaped and you will get < instead of < (etc.). You can use ng-bind-html instead but the returned html will not be bound. You will need to inject the html manually (you can use jquery for this) in your link method and use var compiled = $compile(html)(scope) to bind the result. Then call ele.after(compiled) or ele.replace(compiled) to add it to your page.
I finally get to get it working.
The solution is combined. First of all, I needed to add another directive to parse the element I wanted:
.directive("tableAppendElement", function ($compile) {
return {
restrict: "E",
replace: true,
link: function(scope, element, attrs) {
var el = angular.element("<span />");
el.append(attrs.element);
$compile(el)(scope);
element.append(el);
}
}
})
This will receive the element/text that will be appended and then will registered it to the scope.
However, the problem still exists. How to access the scope of the controller? Since my directive will be used by a lot of controllers, and will depend on the model of the controller, then I just set scope: false. And with that, every method in the controller is now accessible from the directive :D
See the fiddle working here. This also helped me because now, there is no need to pass the transform method, so the controller can be the one handling that as well.

Categories

Resources