Multiple directives appending the same element - angularjs

I'm trying to have a group of directives append to the same DOM element something like this
Angular HTML
<div one two></div>
and the HTML result to be something like this
<div one two>
<h1>one</h1><!--this one added by the 'one' directive-->
<h2>two</h2><!--this one added by the 'one' directive-->
</div>
I'm not sure how is this done with directives without the need to append using Jquery
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one two></div>
</body>
</html>
similar question with no answer
Another question with no answer

You can't do what you want with the template parameter alone. The template parameter is just shorthand for doing something similar to the following in your directive compile function:
compile: function(element, attr) {
element.empty();
element.append(template);
}
If you include a template property on your directive, this will happen automatically. If you want to override this behavior, and just have the directive append the content (without emptying it), you need to simply leave off the template property. So your two directives would be:
.directive('one', function() {
return {
compile: function(element, attr) {
element.append('<h1>one</h1>');
}
};
})
.directive('two', function() {
return {
compile: function(element, attr) {
element.append('<h1>two</h1>');
}
};
});
Which you can see in operation in this Plunk
I've also included a version that uses a template from a file. The only gotcha here is that loading a template in this way requires using link.
.directive('three', function($http, $compile, $templateCache) {
return {
link: function(scope, element, attr) {
$http.get('template.html', {cache: $templateCache}).then(function(result){
element.append($compile(result.data)(scope));
});
}
};
});
And here is a version that uses link.
.directive('one', function($compile) {
return {
link: function(scope, element, attr) {
element.append($compile('<h1>one</h1>')(scope));
}
};
})
.directive('two', function($compile) {
return {
link: function(scope, element, attr) {
element.append($compile('<h1>two</h1>')(scope));
}
};
});
One think you'll notice is that the output is backwards. It seems "one" gets appended after "two", even though it comes first in the markup. If we added the remote version, it would work whenever the template was served remotely, but when it was delivered from the $templateCache it would also be backward.
The reason is they all have the same priority, and "link" is called in the reverse order as compile. To fix, you can simple set the priority of one a little lower (so it get's compiled last, but linked first).
Heres a Plunk that does this with priority.

You cannot have two directives on the same element that will both include a template: only one of them will override the other.
One solution could be to add the two directive within the one's template, as shown below:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1><div two></div>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one></div>
</body>
</html>
Another solution, if you don't want two to be always part of one, is to simply put both next to each other in your HTML, as shown below
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one></div>
<div two></div>
</body>
</html>

Related

bind or print from directive

I am accessing my selected file using a directive as suggested by some SO answers,and i am able to console my file name in the directive.Now i want to bind that name to html from that directive.....how can i do that??
See the p tag after input
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.12/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="file" myfilename />
<P>{{files[0].name}}</p>
</body>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('myfilename', [function () {
return {
link: function (scope, element, attrs) {
element.on('change', function (evt) {
scope.files = evt.target.files;
console.log(scope.files[0].name);
});
}
};
}]);
</script>
</html>
Just add scope.$apply() after scope.files = evt.target.files; in direcives.
The reason why it does not show the updated value immediately is because the 2 way binding updates the parent (or the consumer scope of the directive) scope's bound value only during the digest cycle. Digest cycle happens after the ng-click is triggered. And hence $scope.files in the controller is not yet updated. You can get around this in many ways by using a timeout which will defer the action to run at the end of the digest cycle. You could also do it by setting an object which holds the value as 2-way bound property. Since 2-way bound property and parent scope share the same object reference you will see the change immediately.
Here is the full code
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.12/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="file" myfilename />
<P>{{files[0].name}}</p>
</body>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('myfilename', [function () {
return {
link: function (scope, element, attrs) {
element.on('change', function (evt) {
scope.files = evt.target.files;
scope.$apply();
console.log(scope.files[0].name);
});
}
};
}]);
</script>
</html>

How to make a custom ngIf directive without scope isolation

I want to use ngIf directive and to make my custom myOwnIf directive without scope isolation. As you can see in the code here - it doesn't work for me without scope isolation.
Can someone assist in understanding the problem and the solution?
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
var MyOwnIf = (function () {
/* #ngInject */
function MyOwnIf($element) {
this.isDisplay = $element.attr('my-own-if') === 'true';
}
return MyOwnIf;
}());
app.directive('myOwnIf', function () { return ({
template: "<div ng-if=\"$ctrl.isDisplay\"><ng-transclude></ng-transclude></div>",
transclude: true,
controller: MyOwnIf,
controllerAs: '$ctrl',
//scope: {}, --> If I will uncomment this it will work, but I don't want to isolate the scope
bindToController: true,
restrict: 'A'
}); });
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div my-own-if="false">This text should NOT show because value is FALSE</div>
<div my-own-if="true">This text should show because I value is TRUE</div>
</body>
</html>

Insert directives from custom filters

Is it possible to insert directives from within custom filters?
For example given the following HTML:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.20/angular.js" data-semver="1.3.20"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.0.3/ui-bootstrap-tpls.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello <span ng-bind-html="name | test"></span>!</p>
</body>
</html>
And javascript:
var app = angular.module('plunker', ['ui.bootstrap']);
app.filter('test', ['$sce', function($sce){
return function(val) {
var out = '<b>' + val + '</b>';
out += '<uib-progressbar value="55">55</uib-progressbar>';
return $sce.trustAsHtml(out);
}
}]);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
How do I get the "uib-progressbar" to display properly as a progress bar?
From what I've read I'll need to manually $compile the directive and then append the resulting element to the page but to do that the rest of the html (generated in the filter) will need to be rendered first, so it's kind of impossible from what I can see?
I've set up a plunker here: https://plnkr.co/edit/V5SmmQXPdrfKeVEbNwaV?p=preview
Filetrs are not for this, write directive instead, like:
app.directive('test', function() {
return {
restrict : 'E',
template : function(elem, attrs) {
var out = '<b>' + attrs.val + '</b>';
out += '<uib-progressbar value="55">55</uib-progressbar>';
return out;
}
}
});
https://plnkr.co/edit/oQ0AekzCBjd9eiNqhL6c?p=preview

How to pass value as attribute in angularJs custom directive

I am trying to pass some value as attribute in custom directive and access inside template, but value is coming as blank.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
// $scope.temp = "temp123";
});
app.directive("dirName" , function(){
return {
template: "<div> temp = {{temp}} </div>",
temp:'#',
};
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js" data-semver="1.4.6"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<dir-name temp="my value"></dir-name>
</body>
</html>
Here is the plunker link :
http://plnkr.co/edit/r95YGxS19cMvWMWZMPcN
Please help.
you need to define the scope of the directive then, temp will be a variable within the directive scope.
return {
template: "<div> temp = {{temp}} </div>",
scope: {
temp:'#'
}
};
updated Plunker
if you need to bind the MainCtrl value to directive then use = instead of # as in this Plunker
and there is another one (&) to point to a function within MainCtrl. check this DOC

How can I set the value of an id in the template part of a directive?

I am trying this:
http://plnkr.co/edit/IzhScWwcy6owjsPKm2Fs?p=preview
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
xx<div admin-select
admin-id="examType"></div>xx
</body>
</html>
and
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
});
app.directive("adminSelect", function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
adminId: '=adminId'
},
template: '<div id="{{adminId}}"></div>'
};
});
This is not working and I cannot see why. Can someone give me some advice and help me to set the id of the <div> that's part of the template.
You have to put the value of the admin-id attribute under apostrophe because you want to set the value. Example
<div admin-select admin-id="'examType'"></div>
Otherwise you set the scope variable in the controller and pass this variable to the directive:
app.controller('MainCtrl', function($scope) {
$scope.myId = 'examType'
});
<div admin-select admin-id="myId"></div>
Example
template: '<div id="{{$parent.adminId}}"></div>'
Angular's docs has this "the root of the template always gets a new scope.", maybe that's why.
Upddated
this answer is not working... =)

Resources