How to access element in Angular directive? - javascript

I am trying to set the element's css property top base on it's height. To do it I create a directive like this:
directive('errormsgbox', function($timeout) {
return {
restrict: 'EA',
scope: false,
replace:true,
templateUrl: 'submitter/reports/errormsgbox.html',
link: function(scope,element) {
$timeout(function(){
$timeout(function(){
console.log(element[0].offsetHeight);
},2000);
},2000)
}
};
})
Directive Html:
<span ng-if='expenses.hasBlockers || expenses.hasWarnigs' style='border:1px solid gray' ng-show='policyViolation && showpencil' class='msgbox'>
<ul>
<li ng-repeat='policymsg in expenses.policyMsg'>
<span class='floatleft' ng-class="showPolicyClass(policymsg.type)">{{policymsg.type}} - </span><span style='width:285px;' class='floatleft'>{{policymsg.message}}</span>
<div class='clear'></div>
</li>
</ul>
</span>
Main Html:
<td class="text-center" style='position:relative'>
<errormsgbox></errormsgbox>
</td>// it is in a table cell, the content of directive is from ng-repeat in this table
I need to access <span>.but as you can see, directive works fine it will show the correct info, but every time it will log undefined for element height. any idea How can I access this?

For E directives such as <my-dir></my-dir> the default display value will set to inline. You can do a few things here, either leverage block elements with replace: true or simply set a style rule, in this example, my-dir { display: block }. You could even call elem.css('display', 'block') in your directive link function.
JSFiddle Example - simplified demo
Keep in mind float rules along with inline-block will take margins and padding into consideration for examining offsetHeight

Right click on the page that has the directive you want to access and click 'Inspect Element', then enter:
angular.element($0).scope()
in your developer console. This lets you access all the data in your angular scope. This will help debug the data that angular is using. But, in more direct answer to your question, I have done something similar where I set css properties inside a directive. It would turn your code into something like this:
return {
restrict: 'EA',
scope: false,
replace:true,
templateUrl: 'submitter/reports/errormsgbox.html',
link: function(scope,element,attr) {
element.css({
width: '360px',
height: '640px',
position: 'absolute',
top: '0px',
left: '0px',
background: '#FFF'
});
}
};

Related

AngularJS: Apply ngStyle on Child Element

I have a span that has a ng-include that embeds a svg file. How can I style the svg using ng-style? I want to style it based on its index.
<li ng-repeat="menuItem in menuItems" class="menuitem" ng-class="{'smenuitem': ($index === menuselected)}">
<span class="svgcont2"
ng-include="menuItem.iconurl"
ng-style="getFill($index)"></span>
<span class="listtext">{{ menuItem.name }}</span>
</li>
ng-include is a simple "Dump this content here". There is really no way (using just ng-include) to accomplish this. My suggestion is to create a directive, and attach a directive scope variable like sub-style which inside your directive will set internal styles.
Call it like so:
<span class="svgcont2"
icon-url="menuItem.iconurl"
fill-info="getFill($index)"></span>
Your directive:
app
.directive('svgcont2', function() {
return {
restrict: 'C',
scope: {
fillInfo: '='
},
templateUrl: function(elem, attrs) {
return attrs.iconUrl
}
};
});
In your directive template:
<svg style="{fill: fillInfo}">
Another way, is to do this via styles. If your styles are simple enough, you can add additional classes to your parent span element, which you can propagate to the children inside. See a quick snippet below:
.svgcont2.fill div.childelement
{
//Your style element
}
I got it. still added a fill attribute on the span, then i placed a css style for the svg to inherit the fill of the parent.
.svgcont2 svg {
fill: inherit;
}

In Angular, how can I find the height of a div dynamically and use it in the HTML?

There are several answers here on SO that show how to determine the height of a div dynamically and log it to console. But what I want to do is to get the height of the div and assign it to a style in the HTML:
<div id="myDiv" myHeight>
<ion-scroll direction="y" style="height:{{height}}px">
...
</div>
I need to determine the height of myDiv and set it to the style as shown above.
This is what I tried, but looks like I don't understand directives well enough yet:
.directive('myHeight', function() {
return {
restrict: 'A',
link: function(scope, element) {
scope.height = element[0].offsetHeight;
}
};
})

AngularJs: grab compiled html and set into tooltip

I'm using angular js bootstrap tooltip to show tooltips on a set of elements.
Plunker: http://plnkr.co/edit/9xk41f3CR0wnajN71bSi
I need to inject into the tooltip html compiled by angular, but i don't really get how. The tooltip tutorial is not useful to me because it gets the html from the scope as variable, but for a set of elements this is not possible.
How can i fill tooltip-html-unsafe?
You can do something like this:
HTML:
<li ng-repeat="phone in phones">
<div phone-info index="{{$index}}">
<p tooltip-html-unsafe="{{tooltips[$index] }}">A tooltip should appear on top of this line ({{ phone.name }} - {{ phone.snippet }})</p>
<div>
</li>
Add to controller:
$scope.tooltips = [];
Directive:
app.directive('phoneInfo', function($compile, $timeout) {
/* wrap in root element so we can get final innerHTML*/
var tipTemplate = '<div><p> This will be the content of {{phone.name}} injected in the tooltip </p><div>';
return {
link: function(scope, el, attrs) {
var tipComp = $compile(tipTemplate)(scope)
$timeout(function() {
scope.tooltips[attrs.index] = tipComp.html()
});
}
}
})
Used index to avoid creating an isolated scope. Can also be done with isolated scope and create a property of phone instead of using scope.tooltips[index]
DEMO

AngularJS - Use attribute directive conditionally

I am using "draggable" directive to support image dragging. However, as per the role of the user, I need to disable image dragging for certain groups of users. I have used following code.
<!--draggable attribute is used as handle to make it draggable using jquery event-->
<li ng-repeat="template in templates" draggable id="{{template._id}}" type="template" class="template-box">
<!-- Images and other fields are child of "li" tag which can be dragged.-->
</li>
The method dragSupported is in the template scope and returns true or false. I don't want to create two big duplicate <li> elements by using ng-if for each value returned by dragSupported(). In other words, I am not looking for the following approach to solve this.
<!--draggable attribute is used as handle to make it draggable using jquery event-->
<li ng-if="dragSupported() ==true" ng-repeat="template in templates" draggable id="{{template._id}}" type="template" class="template-box">
<!-- Images and other fields are child of "li" tag which can be dragged.-->
</li>
<!--remove "draggable" directive as user doesn't have permission to drag file -->
<li ng-if="dragSupported() !=true" ng-repeat="template in templates" id="{{template._id}}" type="template" class="template-box">
<!-- Images and other fields are child of "li" tag which can be dragged.-->
</li>
Is there any other approach to avoid code duplicity?
ng-attr-<attrName>
Support for conditionally declaring an HTML attribute is included with Angular as the dynamically-titled ng-attr-<attrName> directive.
Official Docs for ng-attr
Example
In your case, the code might look like this:
<li
id="{{template._id}}"
class="template-box"
type="template"
ng-repeat="template in templates"
ng-attr-draggable="dragSupported() === true"
></li>
Demo
JSFiddle
This contains examples of usage for the following values: true, false, undefined, null, 1, 0, and "". Note how typically-falsey values may yield unexpected results.
Thanks Jason for your suggestion. I took little different approach here. Since I don't want to change the "scope" variable therefore I used "attrs" to check if drag is allowed or not. Following is approach I tool which seems good so far.
Directive code:
app.directive('draggable', function () {
return {
// A = attribute, E = Element, C = Class and M = HTML Comment
restrict: 'A',
replace:true,
link: function (scope, element, attrs) {
if(attrs.allowdrag =="true")
{
element.draggable({
cursor: 'move',
helper: 'clone',
class:'drag-file'
});
}
}
}
});
HTML Code:
<ul>
<!--draggable attribute is used as handle to make it draggable using jquery event-->
<li ng-repeat="template in templates" draggable allowdrag="{{userHasPrivilege()}}" >
<!--Ohter code part of li tag-->
</li>
</ul>
Controller is having implementation of userHasPrivilege().
Not sure if this is correct way or not. Looking for thoughts.
There is no way to directly add or remove an attribute from an element. However, you could create a directive that simply adds the attribute to the element when the condition is met. I've put something together that illustrates the approach.
Demo: http://jsfiddle.net/VQfcP/31/
Directive
myApp.directive('myDirective', function () {
return {
restrict: 'A',
scope: {
canDrag: '&'
},
link: function (scope, el, attrs, controller) {
/*
$parent.$index is ugly, and it's due to the fact that the ng-repeat is being evaluated
first, and then the directive is being applied to the result of the current iteration
of the repeater. You may be able to clean this by transcluding the repeat into the
directive, but that may be an inappropriate separation of concerns.
You will need to figure out the best way to handle this, if you want to use this approach.
*/
if (scope.canDrag&& scope.canDrag({idx: scope.$parent.$index})) {
angular.element(el).attr("draggable", "draggable");
}
}
};
});
HTML
<ul>
<!-- same deal with $parent -->
<li ng-repeat="x in [1, 2, 3, 4, 5]" my-directive="true" can-drag="checkPermissions(idx)">{{$parent.x}}</li>
</ul>
Controller
function Ctl($scope) {
$scope.checkPermissions = function(idx) {
// do whatever you need to check permissions
// return true to add the attribute
}
}
I used a different approach as the previous examples didn't work for me. Perhaps it has to do with using custom directives? Perhaps someone can clear that up.
In my particular example, I'm using ui-grid, but not all ui-grids should use pagination. I pass in a "paginated" attribute and then $compile the directive based on true/false. Seems pretty brutish but hopefully it can push people in a positive direction.
HTML
<sync-grid service="demand" paginated="true"></sync-grid>
Directive
angular
.module('app.directives')
.directive('syncGrid', ['$compile', SyncGrid]);
function SyncGrid($compile){
var nonPaginatedTemplate = '' +
'<div>' +
' <div ui-grid="gridOptions" class="grid"></div>' +
'</div>';
var paginatedTemplate = '' +
'<div>' +
' <div ui-grid="gridOptions" class="grid" ui-grid-pagination></div>' +
'</div>';
return {
link: link,
restrict: 'E',
replace: true
};
function link(scope, element, attrs) {
var isPaginated = attrs['paginated'];
var template = isPaginated ? paginatedTemplate : nonPaginatedTemplate;
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
// Continue with ui-grid initialization code
// ...
}
}

Angularjs how to pass in data using a directive

What I am trying to do is make a function so I can change the height of my ng-grid column width. That is irrelevant besides the fact that the scope from my controller needs to communicate with the scope in my directive.
.directive('getWidth', function(){
return{
controller: 'QuotesCtrl',
link: function(scope){
scope.smRowHeight = function(the value i want){
scope.theRowHeight = the value i want;
}
}
}
})
And I just want to be able to go into my html and say hey for this div I want the height 20
<div getWidth = '20'></div>
I have looking around and I couldn't find anything doing with this exact thing. and by the way, in my QuotesCtrl i initialized the row height like so
$scope.theRowHeight;
Any suggestions?
Try something like this:
.directive('getWidth', function(){
return{
controller: 'QuotesCtrl',
link: function(scope){
console.log(scope.theRowHeight);
},
scope: {
'theRowHeight': '='
}
}
});
Markup:
<div the-row-height="20"></div>
Directives are amazing! You can pass in what is called an isolate scope, and with that you can pass in values as strings or references to your controller scope. There are 3 options on the isolate scope that you should look into. = # & See the link below the example to the docs.
Here is a working JSFiddle
.directive('getHeight', function(){
return{
scope: {
"rowHeight": '='
},
controller: 'QuotesCtrl',
link: function(scope){
scope.smRowHeight = function(the value i want){
scope.theRowHeight = the value i want;
}
}
}
})
You would need to update your html to pass in the new scope value.
<div get-height row-height='20'></div>
More Info on Directives

Categories

Resources