<tr ng-repeat="school in schools">
<td>{{$index+1}}</td>
<td>
<span school-edit>{{school.name}}</span>
</td>
</tr>
my directive
app.directive("school-edit",function()
{
return {
restrict : "A",
link : function(scope,el,atr)
{
el.on("click",function()
{
alert("called");
});
}
};
});
The click event is not firing
I suppose directive is getting registered before ng-repeat populates the table
working plinker http://plnkr.co/edit/XARGhQkZHgOcGZvyiD33?p=preview
OP, appears you naming convention was incorrect. avoid using dashes when defining directive. Opt for camel cased.. myDirective in the js then in the html invoke as my-directive.
Define a directive, this works by using the example I defined.
app.directive('school', [function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
alert("g");
});
}
}
}]);
This will do it for you.
<div school>click me!</div>
if anyone is still looking for an answer, following would be the right way to do it (notice the difference in naming convention):
app.directive("schoolEdit",function()
{
return {
restrict: "A",
link : function(scope,el,atr)
{
el.on("click",function()
{
alert("called");
});
}
};
});
What you are looking for is onclick.
app.directive("school-edit",function()
{
return {
restrict:"A",
link:function(scope,el,atr)
{
el.onclick = function(){
alert("called");
};
}
};
});
Related
I want to write a directive to for a table TBODY to show some text when it is empty. I want to achieve this by writing a directive that detects if the table's TBODY has any child TR, if not then show some text.
I do not wish to use ng-if="model.entries.length == 0" because I might have a TR in there for creating new entry that won't belong to entries.
The directive I wrote currently only works one time because it only runs once. When entries changes the directive won't run again and therefore the empty text is still showing
baseModule.directive('emptyTbody', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
if (element.find('tr').length == 0) {
element.addClass('empty');
} else {
element.removeClass('empty');
}
}
}
});
Is it possible to write a directive that runs when scope changes like regular angular behavior? Or if this cannot be achieved through a directive, is there any other ways to achieve this?
Here is the Html
<tbody empty-tbody>
<tr ng-if="isCreating()">
<td>
<input ng-model="creatingItem.Name"/>
</td>
</tr>
<tr ng-repeat="item in model.entries" >
<td>
<input ng-model="item.Name"/>
</td>
</tr>
</tbody>
Create a isolate scope and put watch there
Like this
baseModule.directive('emptyTbody', function() {
return {
restrict: 'A',
scope: {
source: '='
},
link: function(scope, element, attrs) {
scope.$watch("source", function(nv) {
if (nv) {
if (nv.length == 0)
element.addClass('empty');
else
element.removeClass('empty');
} else
element.addClass('empty');
});
}
}
});
HTML
<tbody empty-tbody source="model.leps">
EDIT
If you wanna to use only from element .
You can use anonymous function to watch.
Try like this
baseModule.directive('emptyTbody', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(function() {
return element.find('tr').length;
}, function(nv) {
if(nv){
console.log("Table has data")
}
else
console.log("Table has no data");
});
}
}
});
JSFIDDLE
Plunker
I'm loading data into a table using ng-repeat.
There is an onFinishRender, which emits ngRepeatFinished. However it's not being fired.
Any ideas why it's not working?
logsmgr.directive('onFinishRender', function ($timeout) {
showLog("onFinishRender");
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
}
});
You need to add the directive to one of your repeat elements. For example, from your Plunker add on-finish-render.
<tr ng-repeat="data in model.DataList" on-finish-render>
I have a directive disable-ng-clicks and under certain conditions, I want to prevent all ng-clicks that are children of the directive. Here is some example markup:
<div disable-ng-clicks> <!-- directive -->
<a ng-click="someAction()"></a>
<div ng-controller="myController">
<a ng-click="anotherAction()"></a>
<a ng-click="moreActions()"></a>
</div>
</div>
If these were normal hyperlinks, I could do something like this in the link function:
function(scope, iElement, iAttrs) {
var ngClicks = angular.element(iElement[0].querySelectorAll('[ng-click]'));
ngClicks.on('click', function(event) {
if(trigger) { // a dynamic variable that triggers disabling the clicks
event.preventDefault();
}
});
}
But this does not work for ng-click directives. Is there another way to accomplish this?
Here is the best I could come up with. I created a new directive to replace ng-click:
directive('myClick', ['$parse', function($parse) {
return {
restrict: 'A',
compile: function($element, attrs) {
var fn = $parse(attrs.myClick);
return function (scope, element, attrs) {
var disabled = false;
scope.$on('disableClickEvents', function () {
disabled = true;
});
scope.$on('enableClickEvents', function () {
disabled = false;
});
element.on('click', function (event) {
if (!disabled) {
scope.$apply(function () {
fn(scope, { $event: event });
});
}
});
};
}
}
}]);
So in a different directive, I can have:
if (condition) {
scope.$broadcast('disableClickEvents');
}
and when I want to re-enable:
if (otherCondition) {
scope.$broadcast('enableClickEvents');
}
I don't like having to use a different directive for ng-click, but this is the best plan I could think of.
You are catching 'click' event on parent only because of JS events bubbling, so if you want to intercept it on all descendants, so your directive should get all descendants of current element, listen their 'click' event and prevent it if necessary.
This directive will iterate over all child elements, check to see if they have an ng-click attribute, and if they do, it will disable any registered click event handlers:
directive('disableNgClicks', function(){
return {
restrict: 'E',
link: function(scope, elem, attrs){
angular.forEach(elem.children(), function(childElem) {
if (childElem.outerHTML.indexOf("ng-click") > -1) {
angular.element(childElem).off('click');
}
});
}
}
})
Plunker demo
I know this is 2 years ago but I needed to do something similar and came up with a rather simple solution.
The object:
items: {
item1 : {
selected: 0,
other: 'stuff'
},
item2 : {
selected : 1,
other: 'stuff'
}
}
The HTML:
<div ng-repeat="item in items" ng-model="item.selected" ng-click="selectParent($event)">
<div ng-click="item.selected ? selectChild($event) : null">Child</div>
</div>
The functions:
$scope.selectParent = function($event) {
var itemScope = angular.element($event.currentTarget)scope().item;
itemScope.selected = !itemScope.selected;
}
$scope.selectChild = function($event) {
$event.stopPropagation;
console.log('I only get triggered if parent item is selected');
}
This is a pretty raw example of what I did. You should probably be using a directive that gives you $scope rather than angular.element($event.currentTarget).scope... either way the simplistic inline if logic is what I was really getting at. You can call a function or not based on some value.
I have an anchor tag that I wish to hide or show depending on a value in the model.
<table>
<tr ng-repeat="item in items">
<td>Other Stuff</td>
<td>
<a href="#/somewhere" ng-show="model.showIt" myCustomDir="some value" onClick="bar(item)" />
</td>
</tr>
</table>
Now in my directive I have the following:
app.directive('myCustomDir', function() {
var def = {
restrict: 'A',
scope: {
onClick: "&"
},
link: function(scope, element, attrs) {
var hover = angular.element("<div><b>Some Text</b></div>");
var button = hover.find('b');
button.on('click', function() {
scope.$apply(function() {
scope.onClick();
})
});
}
};
return def;
})
The problem is as soon as I include my directive the ng-show one I think no longer works and that is because if I am correct it is because my directive works in isolate scope so the model from the parent scope is no longer present.
How would I get my directive to play nicely with ng-show while still being able to let someone what method they want to call when the tag is clicked.
Plunker for all those interested. http://plnkr.co/edit/BLMCgB
You directive creates an isolated scope. So you need to use $parent to get the value of the current repeater item
ng-show="$parent.item.visible"
If you want to make it more generic, you can take the scope off to make it compatible with other directives. Then you can use scope.$eval to call the function passed in.
myApp.directive('myDirective', function ($document) {
var definition = {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('click', function () {
...
button.on('click', function () {
scope.$apply(function () {
scope.$eval(attrs.onClick);
hover.remove();
})
});
});
}
}
return definition;
})
If you want allow any global directive - don't declare private scope.
If you want allow only few directives, add links in scope declaration:
scope: {
onClick: "&",
ngShow : "&"
},
To your question in comments:
Declare controller in directive and declare method in this controller. Then in directive template assign ng-click to this method.
var def = {
restrict: 'A',
controller: function($scope){
$scope.callMe = function(){
console.log('foo');
}
}
}
in template:
<div ng-click="callMe()">content</div>
This method will be accessible only inside your directive.
I have places in my code where I have this:
<input data-ng-disabled="SOME_SCOPE_VARIABLE" />
I would like to be able to use it like this too:
<input data-ng-autofocus="SOME_SCOPE_VARIABLE" />
Or even better, mimicking how ng-style is done:
<input data-ng-attribute="{autofocus: SOME_SCOPE_VARIABLE}" />
Does this exist in the current version of AngularJS? I noticed in the code there's a BOOLEAN_ATTR which gets all the attr's that AngularJS supports. I don't want to modify that in fear of changing versions and forgetting to update.
Update: AngularJS now has an ngFocus directive that evaluates an expression on focus, but I mention it here for the sake of completeness.
The current version of AngularJS doesn't have a focus directive, but it's in the roadmap. Coincidentally, we were talking about this on the mailing list yesterday, and I came up with this:
angular.module('ng').directive('ngFocus', function($timeout) {
return {
link: function ( scope, element, attrs ) {
scope.$watch( attrs.ngFocus, function ( val ) {
if ( angular.isDefined( val ) && val ) {
$timeout( function () { element[0].focus(); } );
}
}, true);
element.bind('blur', function () {
if ( angular.isDefined( attrs.ngFocusLost ) ) {
scope.$apply( attrs.ngFocusLost );
}
});
}
};
});
Which works off a scope variable as you requested:
<input type="text" ng-focus="isFocused" ng-focus-lost="loseFocus()">
Here's a fiddle: http://jsfiddle.net/ANfJZ/39/
You can do this with the built-in ngAttr attribute bindings.
<input ng-attr-autofocus="{{SOME_SCOPE_VARIABLE}}">
The autofocus attribute will be added if SOME_SCOPE_VARIABLE is defined (even if it's false), and will be removed if it's undefined. So I force falsy values to be undefined.
$scope.SOME_SCOPE_VARIABLE = someVar || undefined;
This directive should do the trick:
angular.module('utils.autofocus', [])
.directive('autofocus', ['$timeout', function($timeout) {
return {
restrict: 'A',
scope: {'autofocus':'='}
link : function($scope, $element) {
$scope.$watch 'autofocus', function(focus){
if(focus){
$timeout(function() {
$element[0].focus();
});
}
}
}
}
}]);
Taken from here: https://gist.github.com/mlynch/dd407b93ed288d499778
scope.doFocus = function () {
$timeout(function () {
document.getElementById('you_input_id').focus();
});
};
Create a directive like this
.directive('autoFocus', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $element) {
$timeout(function () {
$element[0].focus();
});
}
}
<input type="text" auto-focus class="form-control msd-elastic" placeholder="">
What I did is using regular autofocus on my inputs: <input autofocus>
And then I set the focus on the first visible input with autofocus when angular is ready:
angular.element(document).ready(function() {
$('input[autofocus]:visible:first').focus();
});
Hope this helps.
I did it with two custom directives, something like this:
(function(angular) {
'use strict';
/* #ngInject */
function myAutoFocus($timeout) {
return {
restrict: 'A',
link: function(scope, element) {
$timeout(function() {
element[0].focus();
}, 300);
}
};
}
function myFocusable() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var focusMethodName = attrs.myFocusable;
scope[focusMethodName] = function() {
element[0].focus();
};
}
};
}
angular
.module('myFocusUtils', [])
.directive('myAutoFocus', myAutoFocus)
.directive('myFocusable', myFocusable);
}(angular));
If you add attribute my-auto-focus to an element, it will receive focus after 300ms. I set the value to 300 instead of 0 to let other async components to load before setting the focus.
The attribute my-focusable will create a function in the current scope. This function will set focus to the element when called. As it creates something in the scope, be cautious to avoid overriding something.
This way you don't need to add something to Angular's digest cycle (watch) and can do it entirely in the view:
<input my-focusable="focusOnInput"></input>
<button ng-click="focusOnInput()">Click to focus</button>
I created a JSFiddle to show the myFocusable directive: http://jsfiddle.net/8shLj3jc/
For some reason I don't know, the myAutoFocus directive does not work in JSFiddle, but it works in my page.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="namesCtrl">
<div ng-repeat="x in names">
<input ng-attr-focus={{$first}} value="{{x.name + ', ' + x.country }}" />
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('namesCtrl', function($scope) {
$scope.names = [
{name:'x1',country:'y1'},
{name:'x2',country:'y2'},
{name:'x3',country:'y3'}
];
});
myApp.directive("focus", function(){
return {
restrict: "A",
link: function link(scope, element, attrs) {
if(JSON.parse(attrs.focus)){
element[0].focus();
}
}
};
});
</script>
</body>
</html>
had created above custom directive for one of my use case.
always focusses on first input element.
works for ajax data, browser back/forward buttons.
Tested on chrome and firefox(default autofocus is not supported here)
JSON.parse is used to parse string "true" returned from html to boolean true in JS.
another way to use attrs.focus === "true" for if condition.
so without $timeout you can also use auto focus like this -
<input type="text" ng-show="{{condition}}" class='input-class'></input>
angular.element(document).ready(function(){
angular.element('.input-class')[0].focus();
});
Combining whar others mentioned above:
JS Code:
myApp.directive('ngAutofocus', ['$timeout', function ($timeout) {
var linker = function ($scope, element, attrs) {
$scope.$watch('pageLoaded', function (pageLoaded) {
if (pageLoaded) {
$timeout(function () {
element[0].focus();
});
}
});
};
return {
restrict: 'A',
link: linker
};
}]);
HTML:
<input type="text" ng-model="myField" class="input-block-level edit-item" ng-autofocus>
Set pageLoaded to true from your initial load method of the page get:
var loadData = function () {
..
return $http.get(url).then(function (requestResponse) {
$scope.pageLoaded = true;
......
}