Fairly new to Angular and still trying to wrap my head around a few things.
I need to get the height of multiple items on a dashboard. I have seen this answer:
Get HTML Element Height without JQuery in AngularJS
However, I can't work out how to get it to work for multiple items. Surely I don't need to write a separate directive for each element.
So playing with this Plunker, I changed the html to below, but get identical values for both elements.
hmm
script.js:
angular.module('myModule', [])
.directive('myDirective', function($timeout) {
return {
restrict: 'A',
link: function(scope, element) {
scope.height = element.prop('offsetHeight');
scope.width = element.prop('offsetWidth');
}
};
})
;
and the html:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.2.13" src="http://code.angularjs.org/1.2.13/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myModule">
<h1 my-directive>Hello Plunker! width: {{width}}, height: {{height}}
</h1>
<h3 my-directive>Smaller Hello Plunker! width: {{width}}, height: {{height}}
</h3>
</body>
</html>
The directives have no scope, so they are storing the values on $rootScope. The values reflect the height and width of the last directive to execute. Fix your directive to use either inherited scope or isolate scope.
angular.module('myModule', [])
.directive('myDirective', function($timeout) {
return {
restrict: 'A',
//set scope to inherited
scope: true,
//OR use isolate scope
//scope: {},
link: function(scope, element) {
scope.height = element.prop('offsetHeight');
scope.width = element.prop('offsetWidth');
}
};
})
;
By setting the scope property, the $compile service will create a new scope (either inherited or isolate) for the directive to store values unique to the directive.
For more information on directive scopes, see AngularJS Comprehensive Directive API -- scope.
I found this script to be very helpful
https://github.com/pocesar/angular-track-height
Related
I have the following code that produces not the expected result:
<div class="outer">1<div>content</div></div>
<div class="outer">2<div>content</div></div>
<div class="outer">3<div>content</div></div>
<div class="outer">4<div>content</div></div>
Instead the result is:
<div class="outer">1</div>
<div class="outer">2</div>
<div class="outer">3</div>
<div class="outer">4<div>content</div></div>
It seems that the ng-repeat gets done first and for the last item it handles the transclude. I know that ng-repeat create the nodes during the compile phase, but I thought in the link phase the link function is called for each node and adds the embedded content.
Can somebody explain what is happening here and how to do it correct?
<!DOCTYPE html>
<html ng-app="Transclude">
<head lang="de">
<meta charset="UTF-8">
<title>Transclude</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
</head>
<body>
<outer ng-repeat="counter in [1,2,3,4]" value="counter">
<div>content</div>
</outer>
<script>
angular.module('Transclude', [])
.directive('outer', function () {
return {
restrict: 'E',
scope: {
value: '='
},
replace: true,
transclude: true,
template: '<div class="outer">{{value}}</div>',
link: function(scope, element, attributes, controller, transclude) {
var transcludedContent = transclude();
element.append( transcludedContent );
}
};
})
</script>
</body>
</html>
transclude() by itself just links the content of the directive to the proper scope and returns it. What you want to do is actually clone the content (make copy of it), before transclude links it. As you code stands right now, your transcluded content is just getting moved from one instance to another - ending up on the last one because, well, it's the last one.
You can do this with the cloneAttachFn. You pass it in to transclude.
link: function(scope, element, attributes, controller, transclude) {
transclude(scope, function(clone) {
element.append( clone );
});
}
I'm just learning angular and creating some simple directives to try some things. I am having (what I think) is a small problem attempting to pass parameters from the directive to a controller function on the root scope.
Please see the following jsfiddle and note that I clicking the button (from within the directive) gives me undefined whereas it seems to work fine if clicking the button from the controller itself.
jsfiddle
Am I just missing something syntax wise? Or am I completely wrong in how this should work? I have made several attempts at placing variables in different locations (note the 'xxx') in the fiddle to see if anything would work and I get either errors or nothing.
<div ng-app="myApp" ng-controller="myController">
<!-- root scope -->
<div style="background-color: teal">
<button ng-click="propertyF('yyy')" >F</button>
</div>
<!-- directive firing methods on the root scope -->
<div style="background-color: coral">
<my-directive3 property6="propertyF()"></my-directive3>
</div>
</div>
var app = angular
.module('myApp', [])
.controller('myController', [
'$scope', function($scope) {
$scope.propertyF = function (aValue) {
alert("propertyF fired: '" + aValue + "'");
};
}
])
.directive('myDirective3', function() {
var directive = {
link : function link(scope, element, attrs) {
console.log("link directive 3");
},
restrict : 'EA',
replace : true,
scope : {
property6: '&'
},
template: '<button ng-click="property6(\'xxx\')">property6</button>'
};
return directive;
});
With the way Angular works, are you passing a function binding and specifying arguments. propertyF() does not specify any arguments.
property6="propertyF(arg)"
Then you can do Angular's unique syntax for handling this:
ng-click="property6({arg:\'xxx\'})"
http://jsfiddle.net/ue1trkt9/1/
This question already has answers here:
Losing scope when using ng-include
(4 answers)
Closed 8 years ago.
I wasn't sure on how to properly title this question, so here it goes ..
I'm trying to setup a dynamic way to change out templates inside of a directive using the ng-include method. I've set up two Plunker examples and although one should work just like the other, that doesn't seem to be the case.
HTML for both Examples:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<main></main>
</body>
</html>
Example #1:
http://plnkr.co/edit/bi3Plrm8xufuN79Nvajj?p=preview
I'm setting up two directives (one main, and one nested as a child):
angular.module('myApp', ['Test']);
angular.module('Test', [])
.directive('main', [
function () {
return {
restrict: 'E',
template: '<input type="text" ng-model="myModel"><br><br><child></child>'
};
}
])
.directive('child', [
function () {
return {
restrict: 'E',
template: '<input type="text" ng-model="myModel">'
};
}
]);
Easy. When running the app both fields populate respectively as the model changes.
Example #2:
http://plnkr.co/edit/3ajcTyfJElEzbqvsWwBM?p=preview
The HTML stays the same, but the js is a little different:
angular.module('myApp', ['Test']);
angular.module('Test', [])
.directive('main', [
function () {
return {
restrict: 'E',
template: '<input type="text" ng-model="myModel"><br><br><child></child>'
};
}
])
.directive('child', [
function () {
return {
restrict: 'E',
controller: function($scope) {
$scope.myTemplate = 'test-template.html'
},
template: "<div ng-include='myTemplate'></div>"
};
}
]);
test-template.html:
<input type="text" ng-model="myModel">
This time, if I interact with the first input that is generated, both inputs update respectively as they should. Here's when it gets interesting ... When/if I interact with the second input (the one generated by ng-include) I loose all binding. Forever... Almost as if it's created its own version of the model. Afterwards, changing the first input has no effect on the second.
What is happening here? Is it indeed creating a new instance of myModel? And if so, how can this be avoided when using this ng-include method?
This is no weird, as PSL said,
ng-include creates new scope.
If you want to create the behaviour that keeps those models attached,
You should change
<input type="text" ng-model="myModel">
To:
<input type="text" ng-model="$parent.myModel">
In my application i have a list of custom directive names.
$scope.data =["app-hello","app-goodby","app-goodafter"];
each name in this array is one directive that im created.
var app = angular.module('app',[]).controller('mainCtrl',function($scope){
$scope.data =["app-hello","app-goodby","app-goodafter"];
}).directive('appHello',function(){
return {
restrict:'EA',
template:'<h1>Hello Directive</h1>'
};
}).directive('appGoodbye',function(){
return {
restrict:'EA',
template:'<h1>GoodBye</h1>'
};
}).directive('appGoodafter',function(){
return{
restrict:'EA',
template:'<h1>Good Afternoon</h1>'
};
});
now i want to load directive with ng-repeat in the view for example because i used EA restrict for directive can create directive in ng-repeat like this :
<div ng-repeat="d in data" >
<div {{d}}></div>
</div>
but this way it doesn't work. so the real question is if i have list of directive how to load this directive with ng-repeat.for this scenario i create a jsbin .
thanks.
You need a "master" directive that $compiles the HTML (optionally containing directives) into an Angular-aware template and then links the compiled element to a $scope:
app.directive('master', function ($compile) {
return {
restrict: 'A',
link: function postLink(scope, elem, attrs) {
attrs.$observe('directive', function (dirName) {
if (dirName) {
var compiledAndLinkedElem =
$compile('<div ' + dirName + '></div>')(scope);
elem.html('').append(compiledAndLinkedElem);
}
});
}
};
});
<div master directive="{{dir}}" ng-repeat="dir in ['dir1', 'dir2', 'dir3']"></div>
See, also, this short demo.
You can do it in this way:
Directive:
app.directive('compile',function($compile){
return{
restrict:'A',
template: '<div></div>',
link:function(scope,elem,attrs){
scope.name = attrs.compile;
elem.children('div').attr(scope.name,'');
$compile(elem.contents())(scope);
}
};
});
HTML:
<div ng-repeat="d in data" compile="{{d}}">
</div>
Jsbin: http://jsbin.com/wofituye/4/edit
I actually prefer to create templates, that just contain the directive. Then you can use ng-include this then enables you to easily pass scope variables into the dynamically chosen directives too.
Here is my widget code fore example:
<div ng-repeat="widget in widgets track by $index" ng-include="widget.url" class="widget-container" ng-class="widget.widget_type.config.height +' ' + widget.widget_type.config.width">
</div>
Then I set the widget.url to a template containing just the right directive.
I can then in my directive do this:
<custom-widget ng-attr-widget="widget"></custom-widget>
Then I have access to the dynamic variable too, so I can access configuration specifics too, without having to dynamically generate HTML strings and compile them. Not a perfect solution, but personally I used to use the other approach mentioned, and discovered that this fit my needs much better.
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