possible for directive to run each time when scope changes? - angularjs

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

Related

Why does my directive fail without errors?

This directive:
angular.module('WizmoApp', [])
.directive('confirmClick', function() {
return {
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = attrs.ngConfirmClick;
// confirm() requires jQuery
if(message && !confirm(message)){
//e.stopImmediatePropagation();
//e.preventDefault();
}
});
}
};
});
, which I'm trying to transcribe from here:
Confirmation dialog on ng-click - AngularJS
is enough to bring my app to a screeching halt.
No display on-screen, no js errors, no nothing, just a blank sacreen.
This is how I'm using it:
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
ng-confirm-click="Are you sure you want to debin this?">
No idea how to debug a directive, let alone write one.
[ EDIT ]
I just noticed that, in the example, the directive is actually called ngConfirmClick. Changed it, but makes no difference.
you are using it wrong
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
confirm-click val="Are you sure you want to debin this?">
and in your directive add scope
angular.module('WizmoApp', [])
.directive('confirmClick', function() {
return {
scope:{
val: '='
},
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = val;
// confirm() requires jQuery
if(message && !confirm(message)){
//e.stopImmediatePropagation();
//e.preventDefault();
}
});
}
};
});
That solution is not working. This error makes no sense:
Syntax Error: Token 'Package' is an unexpected token at column 6 of
the expression [Move Package back to Pile?] starting at [Package back
to Pile?].
This is how I've implemented it:
Directive:
(function(){
angular
.module('WizmoApp')
.directive('confirmClick', function () {
return {
priority: -1,
restrict: 'A',
link: function(scope, element, attrs){
element.bind('click', function(e){
var message = attrs.val;
// confirm() requires jQuery
if(message && !confirm(message)){
e.stopImmediatePropagation();
e.preventDefault();
}
});
}
}
});
})();
View:
<tr class=""
ng-repeat="package in adminManifestVm.Manifest | orderBy:'Id' track by $index"
ng-click="adminManifestVm.debinPackage(package.Id);"
confirm-click
val="Move Package back to Pile?">

Having multiple suffix for same directive (angularjs)

Let say we have the following directive:
app.directive('testList', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
//do stuff
}
}
});
I would like to call this directive from multiple element in html
<button test-list-add></button>
<table test-list="listObject">
<tr ng-repeat="list in testList">
<td test-list-click>list.item</td>
</tr>
</table>
is it possible to get into the link function for each attribute starting with test-list (test-list-add, test-list, test-list-click)?
Thank you
You want to pass attributes to your directive and doing some action, depending on the attribute:
app.directive('testList', function () {
return {
restrict: 'A',
scope: {
'add': '#', // Text
'click': '&' // function
},
link: function (scope, element, attrs) {
// scope.add - your Text you passed
// scope.click(); - calls your function you passed
}
}
});
<button test-list add="test"></button>
<table test-list="listObject">
<tr ng-repeat="list in testList">
<td test-list click="myFunc()">list.item</td>
</tr>
</table>
You can't repeat a directive (what you obvis trying to do).
If you want to make multiple suffix for a directive, you have to declare them one by one:
app.directive('testListAdd', function () {...});
app.directive('testListclick', function () {...});
<test-list-add></test-list-add>
<test-listclick></test-listclick>

click event not firing in Attribute directive inside ng-repeat

<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");
};
}
};
});

onFinishRender not being fired

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>

Access parent data model in directive

I wrote a directive to check for double values in a data column:
The markup
<table>
<tr data-ng-repeat-start="rowItem in vm.model.data" ...>
<td>
<input type="text" data-ng-model="rowItem.ID" data-unique-column="vm.model.data" />
</td>
...
</tr>
</table>
and the directive
(function () {
'use strict';
angular.module('app').directive('uniqueColumn', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.on('keyup blur', function () {
scope.$eval(attrs.uniqueColumn).forEach(function (item) {
// validation logic
});
});
}
};
});
})();
Everything works fine but I asked myself if there was a solution to access the data of my repeater, i.e. vm.model.data, without passing an argument to the directive?
Because you didn't isolate the scope, you can read javascript's prototypal inheritance. You can perhaps access a method from the child that will then lookup to the parent's. And perhaps use the $index as the parameter.
Why not use another directive to define the unique-column's model and add it as a dependency on the unique-column directive, like this:
unique-column-model directive:
app.directive('uniqueColumnModel', function() {
return {
restrict: 'A',
controller: function($scope) {
// add more logic here if necessary
},
link: function (scope, element, attrs, ngModel) {
// save the value of the uniqueColumnModel as a private var in the scope
scope.$uniqueColumnModel = scope.$eval(attrs.uniqueColumnModel);
}
};
});
unique-column directive:
app.directive('uniqueColumn', function() {
return {
restrict: 'A',
// set the uniqueColumnModel directive as a dependency (^ is to search on parents)
require: ['ngModel', '^uniqueColumnModel'],
link: function (scope, element, attrs, ngModel) {
element.on('keyup blur', function () {
// use the private $uniqueColumnModel var that was
// previously saved on the scope
angular.forEach(scope.$uniqueColumnModel, function(item) {
// validation logic
});
});
}
};
});
And on the HTML:
<tr ng-repeat="item in model.items" unique-column-model="model.items">
<td>
<input type="text" ng-model="item.id" unique-column />
</td>
</tr>
Check this plunker.
There's still room for improvement but the idea is there...

Resources