How can I access an elements value using an angular.js directive? - javascript

If I have a directive for a table cell called
<table>
<tr>
<td cellDirective>Some cell Value</td>
<td cellDirective>Another cell value</td>
...
<tr> ...
<table>
defined by
myapp = angular.module('myapp', []);
myapp.directive('cellDirective', function() {
return {
link: function(scope, element) {
console.log(element);
element.addClass("coloring-class");
}
};
});
with the style
<style>
.coloring-class {
color: blue;
}
</style>
What I get in the console is a reference to an object with a ton of different attributes, but I cannot find one with the value in each cell. So how can I access the value inside an element?

As per your JSBin, if you have the cell defined as
<td ng-repeat="cell in row" class="spreadsheet" cell="{{ cell }}">
you can define your directive as
clinApp.directive('cell', function() {
return {
restrict: 'AE',
link: function(scope, element, attrs) {
console.log(attrs.cell);
attrs contains all the attributes in the current element where the directive is placed.

It's just good old fashioned jQuery:
console.log(element.text());

Related

Dynamically select directive based on data

I am trying to populate a table based on an array of objects. This array doesn't contain objects of the same type and for each row I'd like a completetly diferent style and, onclick function, basically a completely different behaviour.
For instance,
var data=[
{
type:'dir-b',
data: { ... }
},
{
type:'dir-b',
data: { ... }
},
{
type:'dir-c',
data: { ... }
}
]
For object type dirB I want a template and controller and for dirC a completely different function and template.
The solution I found was to create 3 directives. One of wich will run to determine one of the other two directives to add based on data.
.directive("dirA", function($compile){
return{
restrict:'A',
priority:1000,
terminal:true,
link: function(scope, element, attribute){
element.removeAttr("dir-a");//prevent endless loop
element.attr(attribute.type,"");
$compile(element)(scope);
}
}
})
.directive("dirB", function($compile){
return{
restrict:'A',
replace:true,
link: function(scope, element, attribute){
console.log("dirA");
}
}
})
.directive("dirC", function($compile){
return{
restrict:'A',
replace:true,
link: function(scope, element, attribute){
console.log("dirC");
}
}
});
Using <tr dir-a type='{{d.type}}' ng-repeat='d in data'/> is not having the desired effect. Either I give dirA a priority of 0 and it can parse the attribute but it's repeated more times than the array size, or I give it a priority of 1000 and it can't parse the b.type and use it as a literal.
Does anyone have a solution for this?
You could potentially use an ngSwitch here.
Plnkr
HTML
<div ng-repeat="(key, d) in data track by $index">
<div class="tbody" ng-switch on="d.type">
<div class="row" ng-switch-when="dir-b" dir-b>{{d}}</div>
<div class="row" ng-switch-when="dir-c" dir-c>{{d}}</div>
</div>
</div>
Then you just define dirB and dirC directives.
This doesn't display as an html table though, you can hopefully work from this though?
Not sure this was the best solution but it was the solution I found.
<table>
<tbody ng-repeat='d in data'>
<tr ng-if='d.type=="dir-b"' dir-b></tr>
<tr ng-if='d.type=="dir-c"' dir-c></tr>
</tbody>
</table>
This way due to ng-if only the correct row will ever be displayed but the problem is that tbody will be repeated as many row as there are in data. But until there is a beter solution this is how I did it.

Ng-repeat using click to show/hide not working properly

This is the directive I'm using, there's a list of names that is dynamically generated from JSON. When you click on a name, it is suppose to show/hide a window with more info on that name. What happens instead is it shows/hides every window for every name in the list. I want it to just show/hide the window for the one I click on.
JS:
app.directive("taskListing", function() {
return {
restrict: 'E',
templateUrl: "/templates/elements/tasklisting.html",
scope: {},
link: function(scope, element, attrs, $sce){
element.on("click", function(){
angular.element("tbody.task-tbody tr").toggleClass("hidden");
});
},
};
});
HTML:
<table class="table" ng-controller="taskController">
<tbody class="task-tbody" ng-repeat="task in tasks" ng-if="task.title != ''">
<tr >
<td>
<span class='tasks-task'>{{task.title}}</span>
</td>
</tr>
<!--This table row is toggled show/hide-->
<tr class="hidden" bgcolor="#F8F8F8" >
<td>
<strong>Description:</strong>
<p>{{task.description}}</p>
</td>
</tr>
</tbody>
</table>
you have wrong query in angular.element("tbody.task-tbody tr") you must specify which tr you want to show
first hide all tr and then show only one with specific ID for example
angular.element("tbody.task-tbody tr").addClass('hidden');
angular.element("#task_8").removeClass('hidden');
specify task id in template:
<tr id="task_{{task.id}}">
It is difficult to be certain without your HTML, but I believe that your issue is angular.element("tbody.task-tbody tr").toggleClass("hidden");.
angular.element(document) aliases a jQuery function (ng docs). In this case it is aliasing a selector and selecting all of the rows in your "tbody.task-tbody tr". Thus, when you are calling .toggleClass("hidden"), jQuery is applying the "hidden" class to all of those elements.
Given that you only want to hide the element that has been clicked on, you can use the provided reference to the element in the directive to apply "hidden" exclusively to that element.
For example:
app.directive("taskListing", function() {
return {
restrict: 'E',
templateUrl: "/templates/elements/tasklisting.html",
scope: {},
link: function(scope, element, attrs, $sce){
element.on("click", function(){
// use element instead of 'angular.element'
element.toggleClass("hidden");
});
}
}
});
I think this will solve your problem.
The element you're listening the click event is the directive itself, so everytime you click on something inside the directive, every <tr> will have the toggleClass performed.
So instead of element.on("click",.... you should do element.find("tbody.task-tbody tr").on("click",....
And if you only want to toggle the visiblity of the <tr> with the #F8F8F8 background, I suggest you add a class to target it more easily.
[edit]
Your link function would be:
function(scope, element) {
element.find("tbody.task-tbody tr").on("click", function() {
this.toggleClass("hidden");
});
}

Add "A"-directive to parent node and execute

I'm a newbie in Angular.js and I stuck with one problem.
I want to integrate this plugin (https://github.com/pratyushmittal/angular-dragtable) to be able to drag columns in my table.
The whole table is a directive. Each <th> also renders by a directive.
<table>
<thead>
<tr>
<th ng-repeat="col in table.columns" my-column></th>
</tr>
</thead>
</table>
According to plugin documentation I need to set draggable directive to table. If I set it manually it doesn't grab my columns properly, because this columns is not rendered at that moment, and this doen't work. In my-column directive I'm waiting for last < th >
.directive('myColumn', ['$timeout', function($timeout) {
return {
restrict: 'A',
templateUrl: 'templates/column.html',
link: function(scope, element, attrs) {
if (scope.$last)
$timeout(function() {
//scope.$emit('lgColumnsRendered');
angular.element(element).closest('table').attr('draggable', 'draggable');
});
}
}
}])
And when last th is rendered I going up to my table and set this directive. For sure it is stupid and doesn't work. I also read about $compile but I need add attribute-directive to already existing table in my DOM.
Maybe I go wrong way and don't understand concept of doing this, but you catch the idea? How can I do this?
The problem is that angular-dragtable doesn't expect that table columns will be dynamic.
And I think it is logical assumption - in most cases table rows will be dynamic (which is OK for the dragtable), but columns are usually static.
The solution to this is to add a special event to the dragtable to ask it for re-initialization when your columns are created, here is the modification I made to dragtable (see the link to the full source below):
project.directive('draggable', function($window, $document) {
function make_draggable(scope, elem) {
scope.table = elem[0];
scope.order = [];
scope.dragRadius2 = 100;
var headers = [];
init();
// this is the event we can use to re-initialize dragtable
scope.$on('dragtable.reinit', function() {
init();
});
function init() {
headers = scope.table.tHead.rows[0].cells;
for (var i = 0; i < headers.length; i++) {
scope.order.push(i);
headers[i].onmousedown = dragStart;
}
}
function dragStart($event) {
Now in your code you can do this:
.directive('myColumn', ['$timeout', '$rootScope', function($timeout, $rootScope) {
return {
restrict: 'A',
templateUrl: 'templates/column.html',
link: function(scope, element, attrs) {
if (scope.$last)
$rootScope.$broadcast('dragtable.reinit');
}
}
Here is a full code of the example I tested the issue on.

Resize on ng-repeat filter

I currently have an AngularJS application embedded in an iframe which needs to be resized to avoid scrollbars. I've got a function in the app that calculates the height of the container and then resizes the iframe.
Currently I am using a directive (resizeAppPart) which will call the resize function on the last item in the scope.
Directive:
app.directive('resizeAppPart', function () {
return function (scope, element, attrs) {
if (scope.$last) {
Communica.Part.adjustSize();
}
}
});
Layout:
<tr ng-repeat="task in filteredTasks = (tasks | filter:filters)" resize-app-part>
<td>{{task.Task_x0020_Title}}</td>
<td><span ng-repeat="user in task.assignees" ng-show="user.Title != ''">
...
</tr>
This works on the initial load but if I filter the list using any of the search boxes, the directive doesn't run so you either end up with a scrollbar or a few thousand pixels of whitespace - neither are ideal.
Is there a way to call the directive, or even the function directly, after the table is filtered?
You need to put a $watch , Use this:
app.directive('resizeAppPart', function ($timeout) {
return function (scope, element, attrs) {
scope.$watch('$last',function(){
Communica.Part.adjustSize();
}
scope.$watch(attrs.filterWatcher,function(){
$timeout(function(){
Communica.Part.adjustSize();
},100)
})
}
});
And slight change in html this way
<tr ng-repeat="task in tasks | filter:filters" resize-app-part filter-watcher={{filters}}>

How can specified table data be animated within a table row with ng-animate?

I'm using Angular's ng-repeat to generate a table with multiple rows. I want to make specific table cells animate when a user mouseovers the table row.
In this example case below, I only want to make the corresponding animated cells be visible (or opacity:1) when the row is mouseovered, but I don't want the the rows to change height (i.e. the row height should account for non-visible cell data).
I have tried CSS animation and ng-animate, but all of my attempts animate all of the rows' corresponding cells (e.g., in a multi-row table where the second column is animated, all cells in the second column will respond when the mouse is over any part of the table).
Full example available in jsBin includes both Greensock TweenMax and css animation attempts.
Relevant html (in this version, only the 2nd-column/red cells change visibility/opacity):
<table class="view-container">
<tr ng-repeat="row in ctrl.rows"
ng-click="fadeIt($index)"
id={{$index}}>
<td>index #{{($index)}}</td>
<td class="animation red" hide-me="isHidden")>red background</td>
<td class="animation blue">blue backgrounds</td>
</tr>
</table>
Relevant js (using TweenMax)
var app = angular.module('app', ['ngAnimate']);
app.controller('MainCtrl', ['$scope', function ($scope) {
$scope.isHidden = false;
$scope.fadeIt = function(id) {
$scope.isHidden = !$scope.isHidden;
};
}]);
app.directive("hideMe", function ($animate) {
return function (scope, element, attrs) {
scope.$watch(attrs.hideMe, function (newVal) {
if (newVal) {
$animate.addClass(element, "fade");
} else {
$animate.removeClass(element, "fade");
}
});
};
});
app.animation(".fade", function () {
return {
addClass: function (element, className) {
TweenMax.to(element, 1, {opacity: 0});
},
removeClass: function(element, className) {
TweenMax.to(element, 1, {opacity: 1});
}
};
});
So I figured out how to do most of what I wanted.
The keys were to:
include display:block!important; inside my .animation class
place a content <div></div> within each <td></td>
place all styling on the interior <div> instead of on the <td>, e.g.
<td> <!-- no styles on the <td>; nothing between the <td> and <div> -->
<div class="animation" ng-show="showDiv">
<!-- all table data goes inside this div -->
</div>
</td>
My working example is available here - http://jsbin.com/wufehu/5/edit?html,css,output

Categories

Resources