AngularJS: Apply ngStyle on Child Element - javascript

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

Related

Change children class when clicking on parent element

Is there a more efficient way with angular to change or add a class to the children of a parent element when the parent is being clicked, other then getting all the childNodes within a function and adding a class for each child ?
<div class="menu-icon-container" ng-click="changeState()">
<div ng-repeat="bar in [bar1, bar2, bar3] track by $index" class="bar{{$index + 1}} "></div>
</div>
The desired result is when menu-icon-container is clicked, the divs within it will toggle a class based on a condition.
You can use the ng-style directive for this. Just set a property on the $scope which you can toggle on click on the div.menu-icon-container
<div class="menu-icon-container" ng-click="conditionForClassName = !conditionForClassName">
<div ng-repeat="bar in [bar1, bar2, bar3] track by $index"
ng-class="{ 'className': conditionForClassName }"></div>
</div>
Docs for ngStyle
you can use directives
DIRECTIVE
myApp.directive('addClassChild', function($timeout) {
var linkFn = function(scope, element, attrs) {
element.bind("click", function() {
for (var i = 0; i < element[0].children.length+1; i++) {
element.children().eq(i).addClass('active');
}
});
};
return {
link: linkFn,
restrict: 'A',
};
})
HTML
<div class="menu-icon-container" add-class-child>
<div ng-repeat="bar in [bar1, bar2, bar3] " class="bar{{$index + 1}} ">
{{$index}}
</div>
</div>
At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.

How to access element in Angular directive?

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

Adding a dynamic directive inside another directive template in AngularJS

I want to add dynamic directive inside another directive tempalte.
As you see I want to add another directive inside a directive template
How do add those dynamic directive there
Please help
return {
restrict: 'AE',
require :'^awkGrid',
templateUrl: 'views/shutter.html',
link : function(scope, element, attr, controllerInstance){
//Set the header
scope.items = [];
angular.forEach(scope.rowData, function(value, key) {
var obj = {
key : key,
value : value
};
template = <country name="'+value.country+'" id="'+key+'"></country>;
scope.items.push(template);
});
};
//Inside shutter.html file
<div data-ng-repeat="item in items" class="ag-row action-row"
ng-class-odd="'ag-row-even'"
ng-class-even="'ag-row-odd'"
ng-class="{'ag-row-selected':$index == selectedRow}"
ng-click="setClickedRow($index,$event)">
<div class="ag-cell">
{{item}} //Not working ,Prinitng the string
<country name="india" id="-1"></country> //Working
</div>
To dynamically switch out the entire template of a directive you have to set the element's html to the desired new template and then pass the element's contents into $compile to bind it with the $scope.
element.html('<h1>Dynamic Content</h1>').show();
$compile(element.contents())($scope);
This should all be defined in the link function of the directive in question.
You must have to recompile your directive. Add folowing code at the end of link function:
$compile(element.contents())(scope);
Answer is in here.
Of course, you have to add service $compile to your directive as a dependency.

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
// ...
}
}

Categories

Resources