I am working in a directive and I am having problems using the parameter element to find its childs by class name.
.directive("ngScrollList", function(){
return {
restrict: 'AE',
link: function($scope, element, attrs, controller) {
var scrollable = element.find('div.list-scrollable');
...
}
};
})
I can find it by the tag name but it fails to find it by class name as I can see in the console:
element.find('div')
[<div class="list-viewport">…</div>,<div class="list-scrollable">…</div>]
element.find('div.list-scrollable')
[]
Which would be the right way of doing such thing? I know I can add jQuery, but I was wondering if wouldn't that be an overkill....
jQlite (angular's "jQuery" port) doesn't support lookup by classes.
One solution would be to include jQuery in your app.
Another is using QuerySelector or QuerySelectorAll:
link: function(scope, element, attrs) {
console.log(element[0].querySelector('.list-scrollable'))
}
We use the first item in the element array, which is the HTML element. element.eq(0) would yield the same.
FIDDLE
In your link function, do this:
// link function
function (scope, element, attrs) {
var myEl = angular.element(element[0].querySelector('.list-scrollable'));
}
Also, in your link function, don't name your scope variable using a $. That is an angular convention that is specific to built in angular services, and is not something that you want to use for your own variables.
Related
I'm trying to call an angular directive with html text I am appending in my controller like so,
var loadReviews=function(){
var theDiv=$("#rlist")
for(var i=0; i<vm.reviewlistByUpvote.length; i++){
var review=vm.reviewlistByUpvote[i];
var html='<a star-directive ng-model="review.overall" data-size="xs" data-disabled="true"> </a>';
theDiv.append(html)
};
};
And my directive looks as follows,
angular.module('App')
.directive('starDirective', function() {
return {
restrict: 'A',
// templateUrl: 'views/star.html',
link: function(scope, element, attrs, ngModel) {
$(element).rating(scope.$eval(attrs.starRating));
// get value from ng-model
ngModel.$render = function() {
$(element).rating('update', ngModel.$viewValue || '');
}
$(element).on('rating.change', function(event, value, caption) {
ngModel.$setViewValue(value);
});
}
};
});
However, the directive won't compile. If I load the star directive within my html, it works fine, but through this approach, nothing gets loaded. I've looked into $compile but it did not fix the issue, but I may have applied it incorrectly.
I would avoid adding manually the directive into the html, I would recommend let angular being in charge of adding html content.
Now there are cases where you will need to append html content (like in modals), in this case you need to compile the html using $compile service before appending the html and then assing a scope (it can be a new one or the same one)
Here is a cool example from Ben Lesh of how to do that:
http://www.benlesh.com/2013/08/angular-compile-how-it-works-how-to-use.html
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
In my app i am trying to call $('#accountTable').dataTable(); this function in my controller. But I think it doesnt work like that in angular.js. Tried to call this function in my Directive but i did not work.
My Directive:
'use strict'
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
$('#accountTable').dataTable();
}
}
});
Angular uses JQuery under the hood if you have JQuery referenced. If you don't then it falls back on a slimmer version of JQuery called JQuery Lite. The elem argument to the link function is already a JQuery wrapped object representing the element your directive is attached to. Just call the plugin from there and it should work fine. It is best to avoid the classic JQuery selectors to navigate the DOM and instead lean on Angular to provide the elements you need.
Make sure you have JQuery referenced before Angular in your script references.
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.dataTable();
}
};
});
Angular needs to know about changes when they happen. If you assign any events and need to update scope variables, you'll need to make sure that Angular knows about those changes by wrapping them in scope.$apply. For example:
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.on('order.dt', function (e) {
scope.something = 'someValue';
}).dataTable();
}
};
});
The above code will set the something property on scope, but because the event was triggered outside of an Angular digest cycle, any UI bound to the something variable may not appear to update. Angular needs to be told of the change. You can make sure the change happens during a digest cycle like this:
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.on('order.dt', function (e) {
scope.$apply(function () {
scope.something = 'someValue';
});
}).dataTable();
}
};
});
Then in your markup:
<table data-data-table-directive>
<!-- table contents -->
</table>
#supr pointed this out in the comments. Note that the attribute is data-data-table-directive not data-table-directive. There is an HTML convention that you can begin arbitrary attributes with data- and Angular respects that and omits it. For example, you can put ng-click on an element or you can put data-ng-click on an element and they would both work the same. It also supports x-ng-click as another convention.
This is super relevant to you because it just so happens that your directive name begins with the word "data", so you'll need to double up on the data- in the beginning. Hopefully that makes sense.
I have a bunch of directives in an html page, let's say the directive is <my-directive></my-directive>. These directives are added or changed outside of AngularJS, e.g. a jQuery function will simply append a new <my-directive></my-directive> to the page.
Here's my question - after appending the new <my-directive></my-directive>, the directive doesn't actually do anything - it's just a tag without any functionality.
How can I force Angular to recognize that a new tag has been added? I've tried mucking around with scope.apply but haven't had any luck.
Thanks!
I think you're looking for angulars $compile method:
myApp.directive('addonclick', function($compile){
return {
restrict: 'A',
link: function(scope, element, attrs){
var html = attrs.addonclick; //or something else
element.on("click", function(){
$(element).append($compile(html)(scope));
})
};
};
});
EDIT:
Try getting the scope first:
var scope = angular.element("yourElement").scope();
Then get and call the compile service:
var compile = angular.element("yourElement").injector().get("$compile"); //or
var compile = angular.injector(["moduleName"]).get("$compile");
$("yourElement").append(compile("<my-directive/>")(scope));
I'm using the AngularUI bootstrap library in my project. In particular, I'm using the accordion and a custom template to display the accordion groups etc. On the template I have an ng-click event which seems to be working only when I don't have the parameter on it. Inside my directive I have a scope variable that produces a unique identifier which I have included as a parameter on the ng-click event. What am I missing to get this working? I'm using Angular 1.0.8.
thanks in advance
!-- template
<a callback="accordion_group_opened($index)" ng-click="callSubmit(currentRow)">
!-- scope variable incremented each time the directive is called
countRow = generateUnique.getNextIdStartingAtOne();
scope.currentRow = countRow;
Edit:
Added the compilation still not getting the value from the scope into my ng-click param. Any ideas on a work around?
compile:function (scope, element, attrs) {
var countRow = generateUnique.getNextIdStartingAtOne();
scope.currentRow = countRow;
return function(scope, element, attr) {
$timeout(function() {
var fn = $parse(attr["ngClick"]);
element[0].on("click", function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
})
};
}