I have a custom angular directive that I conditionally display it, as it's inside ng-if. Inside my custom directive I display html elements, one of them having an absolute positioning.
I'd like to set the absolute element's width to be that of the directive's root width. Please note, I cannot set the directive root's display to be relative.
How would you advise I achieve such a feat?
UPDATE:
Here's a jsfiddle -
http://jsbin.com/qofolahani/edit?html,css,js,console,output
please note that in the link function I don't yet the element's css styling yet and thus width is equal to 0, which means I cannot set it in the child div's width.
Your issue is you use restrict: E.
Browser does not bind getCalculatedStyle method to non-standard DOM entities.
Change this directive to attribute using:
<div my-dir></div>
And directive:
{
restrict: 'A',
link : function (scope, element) {
element.addClass('my-dir');
element.find('.my-dir-content').width(element.width());
},
template: '<div class="my-dir-content"></div>'
}
Or you should use internal templated divs only.
replace: false can help with this.
{
restrict: 'E',
link : function (scope, element) {
var o = element.find('.my-dir');
var i = o.find('.my-dir-content');
i.width(o.width());
},
replace: false,
template: '<div class="my-dir"><div class="my-dir-content"></div>/div>'
}
Or otherwise, replace parent node:
{
restrict: 'E',
link : function (scope, element) {
var i = element.find('.my-dir-content');
i.width(element.width());
},
replace: true,
template: '<div class="my-dir"><div class="my-dir-content"></div>/div>'
}
JSBin Preview
Bonus:
You need a code like this to update width on window resize:
$(window).on('resize', onResize);
function onResize() {
i.width(o.width());
}
scope.$on('$destroy', function(){
$(window).off('resize', onResize);
});
You need to add a css style to the directive root and set this position to relative, something like
custom-directive {
position: relative;
}
Setting the root html element on position: relative will allow the inner html elements to take position absolute in reference to directive.
Then you can take the inner html element and set
inner-html-element {
position: absolute;
width: 100%;
}
Related
I'm trying to validate dynamically generated inputs, but I do not know how to do this.
When I add the div that triggers the directive and inserts the inputs dynamically the div adds the 'has-error' class but does not apply the input style, anyone knows the best way to do this that I'm trying to do?
Here is the markup:
<div ng-if="conditionItem.field.id"
ng-class="{true: 'has-error'}[conditionItem.field.hasError]"
dynamic
input-router
source="conditionItem.field"
ng-click="FieldConditionsCtrl.valueTest(conditionItem.field.hasError)"
ng-required="true"
ng-model="conditionItem.situation[$index]">
</div>
Here is the directive how generate the inputs:
(function() {
'use strict';
angular
.module('applicationInputs', ['rzModule', 'focus-if', 'ui.utils.masks'])
.directive('inputRouter', inputRouter);
/** #ngInject */
function inputRouter($compile){
return {
restrict: 'EA',
scope: {
ngModel: '=',
source: '=',
placeholder: '#',
tabIndex: '='
},
link: function(scope, element, attrs) {
var canvas = angular.element(element[0]);
scope.source.editable = angular.isUndefined(scope.source.editable) ? true : scope.source.editable === true;
//used by setting to override selected field
if (angular.isDefined(attrs.dynamic)) {
scope.$watch('source', function () {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.children().detach();
canvas.append($compile(html)(scope));
});
} else {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.append($compile(html)(scope));
}
}
}
}
})();
Here is my style:
.has-error {
border-color: red
}
Try border: 1px solid red; instead of just setting border-color. Border width is 0 by default, so just setting a color isn't enough
Just a couple nitpicky-subjective style comments also:
element is already a jqLite/jquery element, so no need to call angular.element
the scope.source.editable === true assignment can be shortened to !!scope.source.editable if you really want it to be a boolean.
While clever, this sort of jquery style element building is generally a code smell in angular js. If you really want to go this route, I would build self-contained directives for your inputs and use inputRouter's template to choose. It's easier to understand, so your future self will thank you
{true: 'has-error'}[conditionItem.field.hasError] took me a minute. Just write it as {conditionItem.field.hasError: 'has-error'}
I'm a big fan of these style guides for AngularJS:
John Papa - ES5, but good advice
Todd Motto - ES6+
There's overlap, but take what you like from each.
I set a progress in my app
I want to controll The progress in angular's directive
but how can I change data-value and data-total in directive's link func?
app.html
<div class="ui indicating small progress" data-value="39" data-total="50" plan-progress>
<div class="bar">
<div class="progress"></div>
</div>
</div>
In this html, I want change data-value and data-total
I try this:
app.js
todoApp.directive('planProgress', function() {
return {
link: function(scope, elem, attrs) {
attrs.value = 10
attrs.total = 20
elem.progress();
}
};
});
But it doesn't work
so I want to know how to change it in my directive?
Use attrs.$set() in your link function and recompile the element. Also, don't forget to inject the $compile service to your directive.
In your html you've added the directive as an attribute but didn't mention it in the restrict value in your directive definition. You need to mention it in directive definition.
See the code bellow:
todoApp.directive('planProgress', function($compile) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
attrs.$set('value', 10);
attrs.$set('total', 20);
$compile(elem)(scope);
}
};
});
Simply use :
attrs["data-value"] = 10;
attrs["data-total"] = 20;
You don't want to use attrs.data-total = 20 because the - will force a subtraction.
It's always legal in javascript to use x[keyName] instead of x.keyName, and you must use this second notation when keyName is a strange key such as **$^ùjsls* or data-value. A more useful case is when the key is a variable.
On last thing : as you do, you will always rewrite the coder's inputs. It may have sense, but it's not very elegant.
to know the width of div
i want to know the width of the within the template ,and store in the variable how can i achieve that
pp.directive('skillSlider',function()
{
return{
template:'<div class="progress"></div>'
}
});
----------
how to calculate the width of the div inside the directive template
You can do this with angular.element e.g.:
angular.element(document.getElementById(id)).clientWidth;
angular.element(document.querySelectorAll('.class')[0]).clientWidth;
or if you have jQuery loaded
angular.element($('#id').width();
angular.element($('.class')[0]).width();
you can have the link function to get access to the element using which you can get the width of the div.
pp.directive('skillSlider',function()
{
return{
template:'<div class="progress"></div>',
link:function (scope, element, attrs) {
console.log(element.width());
})
}
});
I'm creating application using AngularJS + ngRoute module + Angular Bootstrap UI. I've created index.html page and few templates, that are being loaded through ng-view directive placed in index.html.
Currently I'm trying to make parallax effect on background images, based on this plugin:
https://github.com/brettdonohoo/angular-parallax
http://brettdonohoo.com/angular-parallax/index.html
In one of my views I have DIV element, that has parallax directive on it.
<div parallax-background parallax-offset="0.4" parallax-ratio="0.7" class="slide" style="background-image: url('{{covers[0].image}}')" ng-click="open(covers[0])"></div>
The problem is - when I'm loading application, the directive is fired correctly on the DIV element, but the background-position css does not change. When I scroll window - background-position changes OK. But I need the position to be applied on initial change too.
When I move my DIV element out of the template to the index.html it works. But of course I would like it to display only one one subpage.
My directive:
.directive('parallaxBackground', ['$window',
function ($window) {
return {
restrict: 'A',
transclude: true,
template: '<div ng-transclude></div>',
scope: {
parallaxRatio: '#',
parallaxOffset: '#',
},
link: function ($scope, elem, attrs) {
var setPosition = function () {
var calcValY = (elem.prop('offsetTop') - $window.pageYOffset) * ($scope.parallaxRatio ? $scope.parallaxRatio : 1.1) - ($scope.parallaxOffset ? $scope.parallaxOffset : 0) * $window.innerHeight;
elem.css('background-position', "50% " + calcValY + "px");
};
angular.element($window).bind('load', function (e) {
setPosition();
$scope.$apply();
});
angular.element($window).bind("scroll", setPosition);
} // link function
};
}
]);
So I have an nvd3 graph in my index.html whose height is set to {{varyingHeight}} like so (code snippet):
<nvd3-line-plus-bar-chart data="data"
showXAxis="true" showYAxis="true"
tooltips="true" interactive="true"
showLegend="true"
height='{{varyingHeight}}'
>
</nvd3-line-plus-bar-chart>
Now in my directive, I have a code which identifies when the height change takes place, and what the new height is:
app.directive('test', function () {
return {
restrict: 'EA',
scope: {},
link: function (scope, element) {
scope.$on('split.resize', function() {
console.log('I got resized');
console.log(element.height());
});
}
};
});
In my controller, I now want to be able to set the new height like so:
$scope.$apply(function(){
$scope.varyingHeight = h;
})
I'm new to angularjs so I can't figure what the best way is to do this. I have seen answers which show how to do this the other way round, i.e from the controller to the directive but this hasn't helped me. How do I pass a element.height() to variable h from the directive to the controller? Or is my code structured wrong in the first place?
You do it by binding the height attribute to the value passed from the controllers scope. Here's an example: http://jsbin.com/vapipizu/1/edit
The important part is that you replace height="{{varyingHeight}}" with height="varyingHeight" and that your directive binds the height attribute like this:
scope: {
height: '='
}
I also faced this issue, I resolved it by passing the height as attribute to the directive
My sample directive
App.directive('aBc',function () {
return {
restrict : 'AE',
scope : {
gridHeight : '#'
},
template : '<div style= "height : {{gridHeight}}px" >'
+'<p>sdtyhdrtydrt-- {{gridHeight}} </p>'
+ '</div>'
};
});
pass the height through directive tag
directly you can pass the height
<div a-bc grid-height="200"></div>
<div a-bc grid-height="500"></div>
<div a-bc grid-height="1000"></div>
or you can set it from your controller
<div a-bc grid-height="someHeight"></div>
initialize someHeight in controller like
$scope.someHeight = 500;