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/
Related
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) {
I want to add the following jQuery function to an existing angularjs application:
$.fn.stars = function() {
return this.each(function(i,e){$(e).html($('<span/>').width($(e).text()*16));});
};
$('.stars').stars();
http://jsbin.com/IBIDalEn/2/edit?html,css,js,output
The html for this should be:
Rating: <span class="stars">4.3</span>
But: where do I have to put the jquery function in order to work with angularjs? And where do I have to call this $('.stars').stars();?
I know this isn't answering your question directly #Michael does a good job of that. However i think its worth noting that for something as simple as this there is no need for jquery. You could roll out your own simple directive and do it right with angular. Plus you leverage data binding to make it update itself.
Plus Michael doesn't answer the issue of where do you extend JQuery to use your custom stars() method? It shouldn't be in the directive otherwise it will be called every time a directive is added to the page. (image if it was in a ng-repeat)
.directive('stars', function () {
return {
restrict: 'EA',
template: '<span class="stars"><span ng-style="style"></span></span>{{stars}}',
scope: {
stars: '='
},
link: function ($scope, elem, attrs){
$scope.$watch('stars', set);
function set(){
$scope.style = {
width: (parseFloat($scope.stars) * 16) + '%'
};
}
}
}
});
Its quite simple you define your template, the two spans. Then you watch the $scope.stars property so you can update your rating should the value change.
see fiddle: http://jsfiddle.net/g7vqb5x9/1/
You should never manipulate the DOM inside a controller. A directive link is the correct spot for DOM manipulation.
Angular.element is already a jQuery object:
angular.module('app', [])
.directive('stars', function() {
return {
restrict: 'C',
link: function($scope, element) {
element.stars();
}
};
});
BTW: a span is an inline element and has NO height and width. you need to use a block element or override the display attribute.
Plunker
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.
I'm using the angular-google-charts [bouil.github.io/angular-google-chart/] directive to create a pie chart.
I can fire a method using this with
<div google-chart chart="myChart" style="{{cssStyle}}" on-select="seriesSelected()"></div>
$scope.seriesSelected = function () {
console.log('item selected');
}
However, I can't work out how to get the key of the selected item. I can see how to do this when using google charts without the angular directive: how to stackoverflow answer. However, I can't follow how to get the element when using angular. This guy seems to have an answer (there is also a plunkr here that fairly accurately shows what I am trying to do), but it seems to be more complex than what I am looking for.
I can see in the ng-google-chart.js directive, there is a line, that adds a property to the selected items:
var selectEventRetParams = {selectedItems:$scope.chartWrapper.getChart().getSelection()};
but I'm not yet able to see how to access this property. Any advice is much appreciated.
Documentation
Just change the html to the following:
<div google-chart chart="myChart" style="{{cssStyle}}" agc-on-select="seriesSelected(selectedItem)"></div>
I couldn't access the directive scope either. So I added a new attribute to the isolated scope and set it "=".
The HTML:
<div google-chart chart="chartObject" style="{{cssStyle}}" custom-select="handleSelect"></div>
Modified directive scope:
scope: {
beforeDraw: '&',
chart: '=chart',
onReady: '&',
onSelect: '&',
select: '&',
customSelect: '='
},
Add this to the "select" listener:
if($attrs.customSelect){
$scope.customSelect(selectEventRetParams);
}
My event handler:
$scope.handleSelect=function(selection){
console.log(selection);
};
http://jsfiddle.net/s911131/sjh4wfe2/5/
Almost there... referring back to your code:
$scope.seriesSelected = function () {
console.log('item selected');
}
Should be changed to:
$scope.seriesSelected = function (selectedItem) {
console.log('item selected');
console.log(selectedItem);
}
In order to pick up the value as passed by the directive.
UPDATE:
This was a doozy. The parameter name 'selectedItem' used in the markup MUST match that being passed back from the directive's isolate scope!!
on-select="doThis(selectedItem)"
https://docs.angularjs.org/guide/directive does mention it, I didn't read properly.
"Often it's desirable to pass data from the isolate scope via an expression to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, the hideDialog function takes a message to display when the dialog is hidden. This is specified in the directive by calling close({message: 'closing for now'}). Then the local variable message will be available within the on-close expression."
ORIGINAL QUESTION:
#Sam - did you ever get this to work? I have set breakpoints both in angular-google-charts and my code and I can see a valid selectedItem variable being constructed and passed into $scope.onSelect({selectedItem: selectedItem}) -
google.visualization.events.addListener($scope.chartWrapper, 'select', function () {
var selectedItem = $scope.chartWrapper.getChart().getSelection()[0];
$scope.$apply(function () {
if ($attrs.select) {
console.log('Angular-Google-Chart: The \'select\' attribute is deprecated and will be removed in a future release. Please use \'onSelect\'.');
$scope.select({ selectedItem: selectedItem });
}
else{
$scope.onSelect({ selectedItem: selectedItem });
}
});
However by the time this reaches my code, the selItem parameter is undefined.
my controller code:
$scope.doThis = function(selItem){
alert("a");
};
my markup:
<div google-chart chart="chartObject" on-select="doThis(selItem)" style="{{cssStyle}}" ></div>
I"ve tried both Angular 1.2.x and 1.4.1 - same behavior in both.
#df1 - I can't see how your solution would work since you are calling a function $scope.customSelect(selectEventRetParams), but your directive's isolate scope has declared customSelect to be bound using '=' instead of '&' for expressions/function callbacks.
I want to improve my answer and to spend more time looking into others' answers. I have a working solution, which is as follows. Modify the directive scope by adding a two way binding called selectedItem:
scope: {
beforeDraw: '&',
chart: '=chart',
onReady: '&',
onSelect: '&',
select: '&',
selectedItem: "="
}
Then my function in the directive is as follows:
google.visualization.events.addListener($scope.chartWrapper, 'select', function (type) {
var selectEventRetParams = { selectedItems: $scope.chartWrapper.getChart().getSelection() };
selectEventRetParams['selectedItem'] = selectEventRetParams['selectedItems'][0];
$scope.selectedItem = selectEventRetParams['selectedItem'].row;
$scope.select({ selectEventRetParams: selectEventRetParams });
}
Then, I have a watch function in my own code, which happens to also be a directive with it's own controller and this code looks like this:
$scope.$watch('selectedItem', function (newValue) {
if (newValue != null) {
$scope.handleSelectedItem();
}
});
The HTML looks like this:
<div google-chart chart="chartObject" style="{{cssStyle}}" sselected-item="selectedItem"></div>
I have actually used several two way bindings and use this to click into the pie chart multiple times, diving into the data. It works really well, but I need to tidy my code somewhat and come back to this.
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.