AngularJS directive using another controller - javascript

I'm trying to create a directive that will output a HTML-template that is using data from a controller.
In sample.js I've added a module, controller and directive
var app = angular.module("myApp", []);
app.controller("MyCtrl", function($scope) {
$scope.someProperty = true;
})
app.directive("myElement", function() {
return {
restrict: "E",
scope: {name: "#"},
template:
'<div ng-show="someProperty">' +
' <p>This element is called {{name}}</p>' +
'</div>',
replace : true,
transclude : false
}
})
I'm using the element with the following HTML
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="sample.js"></script>
</head>
<body ng-controller="MyCtrl">
<div><my-element name="Mark"></my-element></div>
<div><my-element name="Vink"></my-element></div>
</body>
</html>
Since the controller is created in the body, I would expect the child-element to be able to use it's properties/methods. But there's no data showing up (without the ng-show, the data shows fine).
In this simplified sample I could move the ng-show to the DOM-element in the HTML, but in my actual application this wouldn't be an option. I really need my directive to be able to use properties (and methods) from my controller.
Is this possible? And what did I miss to get it to work?

Since you are using an isolated scope you have to declare someProperty to use it like this
app.directive("myElement", function() {
return {
restrict: "E",
scope: {
name: "#",
someProperty: "="
},
template:
'<div ng-show="someProperty">' +
' <p>This element is called {{name}}</p>' +
'</div>',
replace : true,
transclude : false
}
});
and you can use it like this
<my-element name="Vink" some-property="someProperty"></my-element>

Related

Directive showing empty tab

I want to implement directives in my app, but I can't even start with the simplest one. Can someone tell me why?
JS:
angular
.module('app.admin.catalog.nutritional_facts')
.directive('nutritionalInfo', nutritionalInfo);
function nutritionalInfo(){
return{
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world {{ '{{quantity.qty}} {{unit.u}}' }}</div>"
};
}
HTML:
<nutritional-info quantity="{qty:4}" unit="{u:'g'}"></nutritional-info>
I'm somewhat new to Angular, so it is probably the simplest question ever. It only shows the empty label, it doesn't even show the Hello world. I already tried sending ints, strings and objects as attributes.
I've corrected your code as shown below to get your directive working as expected :
Working Fiddle
My Changes :
Corrected directive name to camel case
Corrected the angular binding in html
Corrected the objects syntax that are passed to the directive.
HTML :
<nutritional-info quantity="{qty:4}" unit="{u:'g'}"></nutritional-info>
Controller :
angular.module('app.admin.catalog.nutritional_facts', [])
.directive('nutritionalInfo', nutritionalInfo);
function nutritionalInfo(){
return{
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world {{quantity.qty}} {{unit.u}}</div>"
};
}
You have a few issues:
NutritionalInfo should be nutritionalInfo (camelCase). That's how Angular will know to associate that directive with the <nutritional-info> HTML tag. See Directive Normalization.
You're not passing in objects correctly to the directive. quantity="{qty=4}" should be quantity="{qty: 4}".
You're not evaluating the expression correctly in the template. {{ '{{quantity.qty}} {{unit.u}}' }} can simply be {{quantity.q}} {{unit.u}}. Angular expressions are interpreted like JavaScript code run on the current scope. So, you can even build expressions like {{quantity.q.toFixed(1) + ' ' + unit.u.toUpperCase()}} (resulting in 4.0 G).
With all those fixes, here's a working fiddle.
Here is my full solution:
<!doctype html>
<html lang="en" ng-app="app.admin.catalog.nutritional_facts">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body >
<nutritional-info quantity="{qty: 3}" unit="{u: 'g'}"></nutritional-info>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.js">
</script>
<script>
function nutritionalInfo(){
return {
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world '{{quantity.qty}} {{unit.u}}'</div>"
};
}
angular
.module('app.admin.catalog.nutritional_facts', [])
.directive('nutritionalInfo', nutritionalInfo);
</script>
</body>
</html>

transclude usage in simple Directive Example

In the following simple example I am printing the name model from controller by directive on the view. The example is running fine, but what is the use of transclude I cannot understand. Can someone explain its usage?
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" ></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<people></people>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller declaration
app.controller('myCtrl',function($scope){
$scope.name = "Peter";
});
//directives declaration
app.directive('people',function(){
return{
restric: 'E',
template: '<div>{{name}}</div>',
transclude: true
}
});
</script>
</body>
</html>
Your code doesn't really demonstrate what transclude does:
Look at this plunk and change the true/false value:
Plunk
You will notice the effect now hopefully. The source from plunkr, with a couple of modifications.
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.5.3" data-semver="1.5.3" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<people>HI there</people>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller declaration
app.controller('myCtrl',function($scope){
$scope.name = "Peter";
});
//directives declaration
app.directive('people',function(){
return{
restric: 'E',
template: '<div><ng-transclude></ng-transclude>: {{name}}</div>',
transclude: false
}
});
</script>
</body>
</html>
So when it is true, you will see that the contents are transcluded,
So it says HI There: Peter
When False, it removes the HI There, but keeps the name and my colon:
: Peter
Essentially, these are wrappers around any arbitrary content.
Supposing I have an accordion directive that shows or hides any content that you use it with with an animation.
app.directive('akordion', [function() {
return {
restrict: 'A',
replace: true,
transclude: true,
template: '<div class="accordion-wrapper">'
+'<div class="transcluded" ng-transclude></div>'
+'</div>',
link: function(scope, elem, attrs) {
scope.$watch(attrs.show, function(newVal){
toggle(newVal);
});
function toggle(show) {
var h = (show) ? 0 : '600px';
$(elem).css({ maxHeight: h });
}
}
}
}]);
You'd use it like this:
<div akordion="" id="league-admin">
<div>
foo
</div>
<my-directive></my-directive>
</div>
And the result (generated HTML) is:
<div class="accordion-wrapper" id="league-admin">
<div class="transcluded">
<div>
foo
</div>
<div id="my-directive">...</div>
</div>
</div>
The point is that by calling the akordion="", you take whatever is inside it and put it in the template (<div class="transcluded" ng-transclude>). In other words, the akordion directive wraps over (transcludes) the content you use it on.
Another example would be modal windows. You don't want to repeat the code that defines the modal each time you want to use it, so you define it once, and use transclusion to put any content into it. Check out modal in Bootstrap UI.
Basically If you have some content inside your directive it will be automatically replaced by the directive content
For Example, if you have<people>Test transclude</people> The Test transclude string will be automatically replace by angular when it process the directive. But what if you want 'Test transclude ' also to be displayed ? Here is where transclude come in to action.
Consider the following
app.directive('people',function(){
return{
restric: 'E',
template: '<div><div ng-transclude></div>{{name}}</div>',
transclude: true
}
});
Now the string 'Test transclude' will be also displayed inside tag
And this is the plunker link Plunk

angular directive not rendering

i'm trying to include directives in my application, but even basic directives aren't rendering. what am i doing wrong?
angularloader.js:
var main = angular.module('ngMain', [])
.directive('myScrollable', function () {
return {
restrict: 'AE',
template: '<h3>Hello World!!</h3>'
};
});
My HTML:
<html ng-app dir="auto">
<head>
<meta name="viewport" content="width=device-width" />
<title>#Model.Title</title>
<script src="/Scripts/Libs/angular.js"></script>
<script src="/Scripts/Custom/angularloader.js"></script>
</head>
<body dir="auto">
<my-scrollable></my-scrollable>
</body>
</html>
tried also using tag or attribute ..
you should use module name in ng-app="moduleName"
like:
<html ng-app="ngMain" dir="auto">
if you assign angular module in a variable then use that variable like bellow:
var main = angular.module('ngMain', []);
main.directive('myScrollable', function () {
return {
restrict: 'AE',
template: '<h3>Hello Worlds!!</h3>'
};
});
Working PLUNKER Link
You have define your module but forget to use in html with ng-app= "you module Name". One extra thing you don't need to declare a variable main your code will work without that also.
angular
.module('myModule', [])
.directive('myDir', function{
your code
});

Element height from within an AngularJS directive

I'm trying to get the height of elements in a simple AngularJS app.
See below. What am I doing wrong? The height should be different as the lines wrap, but I get 20 reported back to me regardless of what I input in the "labels" array.
The following code can be executed here, otherwise see below.
http://js.do/code/49177
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<base href="/">
<title>height of element in angularjs</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.8/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.8/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
'use strict';
var app = angular.module('heightApp', ['ngRoute', 'routing']);
app.controller('heightCtrl', ['$scope', function ($scope) {
$scope.labels = [
'Hi there, I\'m a div.',
'Me too, I\'m also a div.',
'Can you see me, because I certainly can\'t see myself. I don\'t even know my own height. Isn\'t that just crazy?'
];
}]);
angular.module('routing', []).config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'height.html',
controller: 'heightCtrl'
});
}]);
angular.module('heightApp').directive('reportMyHeight', function() {
return function (scope, el, attrs) {
alert('offsetHeight = ' + el[0].offsetHeight);
}
})
</script>
</head>
<body ng-app="heightApp">
<div class="container">
<div ng-view></div>
</div>
</body>
<script type="text/ng-template" id="height.html">
<div class="row">
<div class="col-sm-4" report-my-height ng-repeat="lbl in labels">
{{ ::lbl }}
</div>
</div>
</script>
</html>
You need to wait till the next digest cycle. When you do it right away in the directive the interpolations {{ ::lbl }} inside the ng-repeat would not have expanded yet. You can place it in a $timeout turning off the applyDigest argument.
i.e, example:
angular.module('heightApp').directive('reportMyHeight', function($timeout) {
return function (scope, el, attrs) {
$timeout(init, false);
//Initialization
function init(){
console.log('offsetHeight = ' + el[0].offsetHeight, el.html());
}
}
});
Plnkr
Another way to make sure you get the height of the element is to use watch.
angular.module('heightApp').directive('reportMyHeight', function($timeout) {
return function (scope, el, attrs) {
scope.$watch('lbl', function(newval, oldval){
alert(newval + '\n\n' + 'offsetHeight = ' + el[0].offsetHeight);
});
}
})
It will only be triggered once since you use ::.

Change page TITLE tag to match page's H1 tag in AngularJS

How would I dynamically change the page's title tag based on my page's H1 tag in AngularJS.
I know in jQuery I could do something like:
var title = $('#content').find('h1').first().text();
if (title.length >= 1)
{
document.title = 'My Site Name | ' + (title);
}
else {
document.title = 'My Site Name';
}
What is the best way to accomplish the same thing in AngularJS?
I would rather not put it in the app.js because to me it seems wrong to be putting content -- which may change -- mixed with the code. If I edit the text of the H1 in my partial view it needs to automatically change the title.
Assuming you want to bind the title to static h1 content, you can use the following directive:
var app = angular.module('bindTitle', []);
app.directive('bindTitle', ['$document', function($document) {
return {
restrict: 'A',
link: function(scope, iElement) {
$document[0].title += ' | ' + iElement.text();
}
};
}]);
The usage will be:
<html>
<head>
<title>My Site Name</title>
<head>
<body>
<h1 bind-title>Your title</h1>
</body>
</html>
And Your title will be appended to the page title on page load. i.e. My Site Name | Your title
Edit: auto append title on h1 element
var app = angular.module('bindTitle', []);
app.directive('h1', ['$document', function($document) {
return {
restrict: 'E',
link: function(scope, iElement) {
$document[0].title += ' | ' + iElement.text();
}
};
}]);
And content inside <h1> elements will be appended to the page title.
It is there in the documentation,
Try something like this
angular.module('documentExample', [])
.controller('ExampleController', ['$scope', '$document', function($scope, $document) {
$scope.title = $document[0].title;
$scope.windowTitle = angular.element(window.document)[0].title;
}]);
http://plnkr.co/edit/rlq032m5KTVLSRMDRHvr?p=preview
You can use Angular's data binding on the <title> element with either ng-bind or ng-bind-template.
Of course, you need to have the desired value in a variable on your module/controller (e.g., $scope) to use data binding. If the value isn't part of the scope, Angular can't offer any "better" solution than jQuery or standard JavaScript (you'll need to find the <h1> tag on the page and extract the text in some fashion regardless).
angular.module('app', [])
.controller('MyCtrl', function($scope) {
$scope.title = 'My Page';
});
<html ng-app="app" ng-controller="MyCtrl">
<head>
<!-- Direct binding example -->
<title ng-bind="title"></title>
<!-- Or, template example -->
<title ng-bind-template="My Site{{ title.length > 0 ? ' | ' + title : ''}}"></title>
You can use $watch to change the title
<body ng-app="app">
<div ng-controller="demo">
<h1>{{title}}</h1>
<input type="text" ng-model="title" />
</div>
</body>
<script>
angular.module('app', []).controller('demo', ['$scope', function($scope) {
$scope.$watch($scope.title, function() {
document.title = $scope.title;
});
}]);
</script>
I think the easiest way to do this is to set same model for your title and for your h1
Take this jsFiddle link as an example. When you are writing something in that textbox, name is modified in 2 places, in the same time.
Note: You should do this in a MasterController for your Layout html page.

Categories

Resources