Passing ng-* attributes through to an Angular directive - javascript

I've got an Angular directive <my-button> in which I need to have it run another directive (my-fun-directive) on it's output, which is why I'm using $compile instead of a directive template. Unfortunately it appears that doing it this way does not allow any additional HTML attributes or ng-*attributes to be passed through.
Directive
app.directive('myButton', function ($compile) {
return {
restrict: 'E',
replace: true,
scope: true,
link: function (scope, element, attrs) {
var btnTxt = attrs.text || "";
scope.buttonInnerHtml = attrs.icon ? '<span class="glyphicon glyphicon-' + attrs.icon + '"></span> ' + btnTxt : btnTxt;
var template = '<button type="button" class="myCustomClass" ng-bind-html="buttonInnerHtml" my-fun-directive></button>';
var content = $compile(template)(scope);
element.replaceWith(content);
}
};
});
Usage
<my-button
icon="ok"
text="Save Changes"
class="anotherClass"
ng-hide="someProperty"
ng-click="myClickEvent()"
example-directive></my-button>
Current Output (line breaks added for readability)
<button
type="button"
class="myCustomClass"
ng-bind-html="buttonInnerHtml"
my-fun-directive>
<span class="glyphicon glyphicon-ok"><span> Save Changes
</button>
Desired Output (line breaks added for readability)
<button
type="button"
class="myCustomClass anotherClass"
ng-bind-html="buttonInnerHtml"
ng-hide="someProperty"
ng-click="myClickEvent()"
my-fun-directive
example-directive>
<span class="glyphicon glyphicon-ok"><span> Save Changes
</button>
Note the inclusion of the ng-* attributes, the additional directive, and the added CSS class. How can I get all of this to work together?

The problem was in HTML content of buttonInnerHtml. I got error "Attempting to use an unsafe value in a safe context.". When I fixed this all works fine:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.js"></script>
</head>
<body ng-app="plunker" ng-controller="MainCtrl">
<my-button
icon="ok"
text="Save Changes"
class="anotherClass"
ng-hide="someProperty"
ng-click="myClickEvent()"
example-directive></my-button>
</body>
<script>
var app = angular.module('plunker', []).directive('myButton', function ($compile, $sce) {
return {
restrict: 'E',
replace: true,
scope: true,
link: function (scope, element, attrs) {
var btnTxt = attrs.text || "";
scope.buttonInnerHtml = attrs.icon ? '<span class="glyphicon glyphicon-' + attrs.icon + '"></span> ' + btnTxt : btnTxt;
scope.buttonInnerHtml = $sce.trustAsHtml(scope.buttonInnerHtml);
var template = '<button type="button" class="myCustomClass" ng-bind-html="buttonInnerHtml" my-fun-directive></button>';
var content = $compile(template)(scope);
element.replaceWith(content);
}
};
}).controller('MainCtrl', ['$scope', '$http', function($scope, $http) {
}]);
</script>
</html>

Related

How to pass a custom directive attribute to custom directive child element in Angular 1.5?

I'm currently trying to pass a validation directive to a custom element directive. But I'm struggling to make it work since it should receive model as an input while I am using bind to controller.
I have to premise that I cannot upgrade to a more recent version of Angular, so 1.5 is the limitation, together with the fact I cannot edit validation directive.
I thought transclude would have helped but with directive attribute it looks not so promising.
What the following code should do is to validate vm.model on input element.
Here's the HTML:
<body ng-controller="MainCtrl">
<div class="myClass">
<my-custom-directive data-placeholder="No text"
data-id="myModel.id"
data-model="myModel.text"
not-editable-directive-attribute >
</my-custom-directive>
</div>
</body>
And here the app.js:
var myTemplate = '<div class="myContainer">' +
'<input class="myInput"' +
' ng-mousedown="$event.stopPropagation();"' +
' ng-show="vm.isFocused"' +
' ng-model="vm.model"' +
' ng-change="vm.onChange()"' +
' type="text">' +
'<span ng-show="!vm.isFocused">{{vm.model}}</span>' +
'<span ng-show="!vm.isFocused && !vm.model && vm.placeholder">{{vm.placeholder}}</span>' +
'</div>';
app.controller('MainCtrl', function($scope) {
$scope.myModel = {
id: 'test',
text: 'this is text'
};
});
app.directive('myCustomDirective', ['$timeout', function($timeout) {
return {
restrict: 'E',
replace: true,
template: myTemplate,
controllerAs: 'vm',
bindToController: {
id: '#',
model: '=',
onChange: '&',
placeholder: '#'
},
scope: {},
controller: angular.noop,
link: function(scope, element) {
var input = element.find('input')[0];
var spans = Array.from(element.find('span'));
var vm = scope.vm;
vm.isFocused = false;
vm.focus = function() {
vm.isFocused = true;
scope.$applyAsync(function() {
$timeout(function() {
input.focus();
input.select();
});
});
};
spans.forEach(span => span.addEventListener('click', vm.focus));
}
};
}]);
app.directive('notEditableDirectiveAttribute', [function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.myCustomDirectiveAttribute = function(modelValue, viewValue) {
if (viewValue) {
return viewValue.indexOf('e') < 0;
}
return false;
};
}
};
}]);
I've created a plunker to make it clearer:
http://plnkr.co/edit/auminr?p=preview
So clicking on span element i should be able to edit text and directive should validate it (in this specific case check if it contains letter "e").
Is it even possible or am I struggling against windmills?
One approach to adding directives to templates based on component attributes is to use the function form of the template property:
<my-custom-directive data-placeholder="No text"
model="vm.data"
custom="not-editable-directive-attribute" >
</my-custom-directive>
app.directive("myCustomDirective", function() {
return {
template: createTemplate,
scope: {},
//...
});
function createTemplate(tElem, tAttrs) {
var placeholder = tAttrs.placeholder;
var model = tAttrs.model;
var custom = tAttrs.custom;
return `
<input placeholder=${placeholder}
ng-model=${model}
${custom} />
`;
}
})
The createTemplate function copies attributes and used them in a template literal.
For more information, see
AngularJS Comprehensive Directive API - template
MDN JavaScript Reference - Template Literals

html tags not displaying using ng-bind-html in Angularjs

I have html template like this:
I want to bind this template using "ng-bind-html", like below:
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$compile',
function($scope, $compile) {
var templateHTML =
'I am an <code>HTML</code>string with ' +
'<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()"></i></span>';
$scope.myHTML = $compile(templateHTML)($scope);
}
]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular-sanitize.js"></script>
<div ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html-unsafe="myHTML"></p>
<p ng-bind-html="myHTML"></p>
</div>
</div>
nothing I'm getting.
How to fix this.
I think a possible solution here is to write your own directive like
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$compile', function ($scope, $compile) {
var templateHTML =
'<div>I am an <code>HTML</code>string with ' +
'<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()">i</i></span></div>';
$scope.myHTML = templateHTML;
$scope.refresh = function () {
console.log('refresh')
};
}]);
angular.module('bindHtmlExample').directive('myHtml', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function link($scope, $element, attrs) {
attrs.$observe('myHtml', function (value) {
var $el = $compile(value)($scope);
$element.empty().append($el)
})
}
}
}])
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.3/angular-sanitize.js"></script>
<div ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html-unsafe="myHTML"></p>
<p ng-bind-html="myHTML"></p>
<p my-html="{{myHTML}}"></p>
</div>
</div>
According to the Vojta's comment in this issue:
qLite throws when given a string that does not start with "<", it should trim the string first.
In other words, your HTML string have to be an "element" with an opening and closing tags.
Put the HTML string you have into a div container:
var templateHTML =
'<div>I am an <code>HTML</code>string with ' +
'<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()"></i></span></div>';
Also see:
$compile fails on leading whitespace of template string
When you use $complie it will return a jqLite object, not a HTML string. However, the value of the variable for ngBindHTML should be a string including HTML. That's why you can see nothing as result.
For your situation, it's better to use Directive than Controller to insert your HTML. See my Codepen: http://codepen.io/jennieji/pen/jhnCk
JS:
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$interpolate',
function($scope, $interpolate) {
var templateHTML =
'I am an <code>HTML</code>string with ' +
'<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()">{{ bindText }}</i></span>';
$scope.bindText = 'Refresh()';
$scope.refresh = function() {
console.log('refresh() runned.');
};
$scope.interpolate = $interpolate(templateHTML)($scope);
}
])
.directive('exampleDirective', ['$compile', function($compile){
return function(scope, element){
var templateHTML =
'<p>I am an <code>HTML</code>string with ' +
'<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()">{{ bindText }}</i></span></p>';
element.append( $compile(templateHTML)(scope) );
};
}]);
HTML:
<div ng-app="bindHtmlExample">
<div ng-controller="ExampleController" example-directive>
<p ng-bind-html-unsafe="interpolate"></p>
<p ng-bind-html="interpolate"></p>
</div>
</div>

I'm having trouble replicating simplest of javascript code using angular directive

I'd written a very simple widget/snippet. Five stars (using font-awesome) on hover, replace empty star with filled star. On mouse out go back to default no. of star if there is a default value, and on-click change the default value depending on the star clicked (for example click on 4th star would change the value to 4 and so on. Really simple stuff. I can't for the life of me replicate it using angular js...
I know I need to do it using directives and transclusion if I understand it correctly. I'm having so much trouble even getting variable no. of filled and empty stars based on default value....
I'd appreciate it if someone could direct me.. here's the code.
Html stuff
<div class="ratingList" rating-widget rate='{{ rating }}' increment="increment()">
<span>Hate it</span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span>love it</span>
very basic controller
bmApp.controller('MainController', ['$scope', function($scope){
$scope.rating = 3;
$scope.increment = function(){
$scope.rating = $scope.rating + 1;
}
}]);
culprit directive
bmApp.directive('ratingWidget', function(){
return{
restrict: 'A',
replace:true,
transclude:true,
template: '<div><button ng-click="increment()">Click</button><div class="rating"></div></div>',
controller:['$scope', '$element', '$transclude', function($scope, $element, $transclude){
$transclude(function(clone){
var stars = clone.filter('.star');
var filledStar = $('<span class="star"><i class="fa fa-star fa-lg"></i></span>');
var container = $element.find('.rating');
angular.forEach(stars, function(val, key){
var star = $(val);
if(key<$scope.rate)
{
//console.log(key);
container.append(filledStar);
//star.replaceWith(filledStar);
//angular.element(star.children()[0]).removeClass('fa-star-o').addClass('fa-star')
}else{
//console.log(key);
container.append(star);
}
});
});
}],
scope:{
rate:'#',
increment:'&'
}
}
});
I'm stuck at the very begining, can't show filled stars based on default value... The append is resulting in 3 stars...
There are a few different ways of being able to handle this sort of functionality.
I've updated your example to show the use of the isolate scope and transclusion (for the increment() button).
We also bundle the star markup into the ratingWidget directive to make it modular and keep it as more of a standalone component.
You can see that because of the ng-repeat and ng-class directives we don't have to work directly with HTML elements if we don't want to, Angular handles the heavy lifting through data binding.
Here is a plunker: http://plnkr.co/edit/hd5DLOpRC3R9EFy316Gl?p=preview
(If you look at the history on that Plunker you will see how I was using jQuery to manipulate the elements/classes directly)
HTML:
<div ng-app="bmApp">
<div ng-controller="MainController">
<div rating-widget rate="rating" max-rating="maxRating">
<!--
This is the content that will be transcluded.
Transclusion means that this content will linked with
the parent scope instead of being linked into the
scope of the `ratingWidget`.
i.e. the `increment()` function is defined in `MainController`
not in the `ratingWidget`.
-->
<button ng-click="increment()">Click</button>
</div>
</div>
</div>
JavaScript:
var bmApp = angular.module('bmApp', []);
bmApp.controller('MainController', ['$scope',
function($scope) {
$scope.rating = 3;
$scope.maxRating = 6;
$scope.increment = function() {
if ($scope.rating < $scope.maxRating){
$scope.rating += 1;
}
}
}]);
bmApp.directive('ratingWidget', function() {
return {
restrict: 'A',
transclude: true,
scope: {
rate: '=',
maxRating: '='
},
link: function(scope, element, attr){
var classes = {
empty: 'fa-star-o',
full: 'fa-star'
};
scope.stars = [];
scope.$watch('maxRating', function(maxRating){
maxRating = maxRating || 5;
scope.stars.length = maxRating;
for (var i = 0, len = scope.stars.length; i < len; i++){
if (!scope.stars[i]){
scope.stars[i] = {
cssClass: classes.empty
};
}
}
updateRating(scope.rate);
});
scope.$watch('rate', function(newRating){
updateRating(newRating);
});
scope.selectRating = function(index){
// The $index is zero-index but the ratings
// start at one, so add 1.
scope.rate = index + 1;
}
function updateRating(rating){
rating = rating || 0;
for (var i = 0, len = scope.stars.length; i < len; i++){
var star = scope.stars[i];
if (i < rating){
star.cssClass = classes.full;
} else {
star.cssClass = classes.empty;
}
}
}
},
template: '<div>' +
'<div class="ratingList">' +
'<span>Hate it</span>' +
'<span class="stars">' +
'<span class="star" ng-click="selectRating($index)" ng-repeat="star in stars track by $index"><i class="fa fa-lg" ng-class="star.cssClass"></i></span>' +
'</span>' +
'<span>love it</span>' +
'</div>' +
'<div ng-transclude></div' +
'</div>'
}
})
Edit:
#dan-tang
Yes, if you had the button outside the directive but inside MainController it would all work as expected and you wouldn't need transclude.
But the point is that the button is inside the directive and calling a method defined on MainController. To do that we need to transclude the content so that binds to the parent scope.
Here's a plunker showing this example: http://plnkr.co/edit/x9xZwve9VkwbTGKUGjZJ?p=preview
HTML:
<div ng-controller="MainCtrl">
<div>I am: {{name}}</div>
<div widget>
<!--
Without transclusion this will say 'widget', with transclusion this will say 'controller'.
Transclusion lets us control the scope to which these expressions are bound.
-->
<div>I am: {{name}}</div>
</div>
</div>
JavaScript:
testApp.controller('MainCtrl', ['$scope', function($scope){
$scope.name = 'controller';
}]);
testApp.directive('widget', function(){
return {
scope: true,
transclude: true,
link: function(scope, element, attr){
scope.name = 'widget'
},
template: '<div>' +
'<div>I am: {{name}}</div>' +
'<div ng-transclude></div>' +
'</div>'
}
});
I would say that transclude in Angular is like a closure in JavaScript - it lets you control the scope to which variables and expressions are bound.
Here's a rough JavaScript analogue of the example above to show some of the similarities between the two concepts:
var name = 'controller';
var printCallback = function(){
console.log('name=' + name);
}
function Widget(printCallback){
var name = 'widget';
this.printName = function(){
console.log('name=' + name);
printCallback();
}
}
var widget = new Widget(printCallback);
widget.printName();
// Prints:
// name=widget
// name=controller
A ratings system customize level and easy installation , the best I found: https://rating-widget.com/get/rating/

AngularJS - 3-button group acting as radio buttons

Using the Ionic framework, I'm trying to create a group of three buttons that act as radio buttons:
If I click on Breakfast, I would like Lunch and Dinner to return to their normal (white) state, and Breakfast to turn Blue.
With my current code, I can't get this functionality to work, although I can get the buttons to switch color, slightly randomly (perhaps I just don't understand the ng-class directive).
Here is my HTML code:
<div class="bar bar-subheader">
<div class="button-bar">
<a class="button" ng-class="{'button-positive' : !isActiveB, 'none': isActiveB}" ng-click="active('breakfast')">Breakfast</a>
<a class="button" ng-class="{'button-positive' : !isActiveL, 'none': isActiveL}" ng-click="active('lunch')">Lunch</a>
<a class="button" ng-class="{'button-positive' : !isActiveD, 'none': isActiveD}" ng-click="active('dinner')">Dinner</a>
</div>
</div>
My JS:
$scope.active = function(meal) {
switch (meal) {
case 'breakfast':
$scope.$broadcast('slideBox.setSlide', 0);
$scope.isActiveB = $scope.isActiveB;
$scope.isActiveL = !$scope.isActiveL;
$scope.isActiveD = !$scope.isActiveD;
break;
case 'lunch':
$scope.$broadcast('slideBox.setSlide', 1);
$scope.isActiveB = !$scope.isActiveB;
$scope.isActiveL = $scope.isActiveL;
$scope.isActiveD = !$scope.isActiveD;
break;
case 'dinner':
$scope.$broadcast('slideBox.setSlide', 2);
$scope.isActiveB = !$scope.isActiveB;
$scope.isActiveL = !$scope.isActiveL;
$scope.isActiveD = $scope.isActiveD;
break;
}
};
I can put the code in JSFidle if you require more information and a working solution.
Thanks for your help.
NOTE: I would like to maintain my active() function, and use the ng-class directive if possible, as I have a lot of other code dependent on this function.
Maybe this simplified example will help you a little:
angular.module('plunker', []).controller('MainCtrl', function($scope) {
$scope.active = 'breakfast';
$scope.setActive = function(type) {
$scope.active = type;
};
$scope.isActive = function(type) {
return type === $scope.active;
};
});
<link rel="stylesheet" href="http://code.ionicframework.com/0.9.26/css/ionic.min.css">
<script src="http://code.angularjs.org/1.2.13/angular.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl" class="bar bar-subheader">
<div class="button-bar">
<a class="button" ng-class="{'button-positive': isActive('breakfast')}" ng-click="setActive('breakfast')">Breakfast</a>
<a class="button" ng-class="{'button-positive': isActive('lunch')}" ng-click="setActive('lunch')">Lunch</a>
<a class="button" ng-class="{'button-positive': isActive('dinner')}" ng-click="setActive('dinner')">Dinner</a>
</div>
</div>
Demo: http://plnkr.co/edit/9HmuTStz70x5KoAvLaP4?p=preview
Here is a more flexible solution for future Googlers.
Working plunker:
http://plnkr.co/edit/U2Hvx4?p=preview
.directive('barSelect',function($parse){
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
value: '=barSelect'
},
link: function(scope, element, attrs, ngModelCtrl){
element.addClass('button');
element.on('click', function(e){
scope.$apply(function(){
ngModelCtrl.$setViewValue(scope.value);
});
});
scope.$watch('model', function(newVal){
element.removeClass('active');
if (newVal === scope.value){
element.addClass('active');
}
});
}
};
});
And a usage example:
<div class="button-bar">
<a bar-select="button.value"
ng-repeat="button in clientSideList"
ng-model="data.clientSide"
>{{button.text}}</a>
</div>
Here's another alternative approach which combines the other two here. It requires just a single <button-group> element with the following attributes:
ng-model
buttons - array of objects containing 'text' and 'value' properties
button-class - optional string containing CSS class(es) to apply to the rendered links, in addition to the default 'group-btn' and 'group-btn-active' classes
.
.directive('buttonGroup',function($parse){
return {
restrict: 'E',
require: 'ngModel',
scope: {
model: '=ngModel',
buttons: '=',
buttonClass: '='
},
template: '<a class="group-btn {{buttonClass}}" ' +
' ng-repeat="button in buttons" ' +
' ng-class="{\'group-btn-active\': isActive(button.value)}" ' +
' ng-click="buttonClicked(button.value)"> ' +
' {{button.text}} ' +
'</a>',
controller: ['$scope', function($scope) {
$scope.buttonClicked = function(value) {
$scope.value = value;
};
$scope.isActive = function(value) {
return $scope.value === value;
};
}],
link: function(scope, element, attrs, ngModel) {
element.on('click', function(e){
scope.$apply(function(){
ngModel.$setViewValue(scope.value);
});
});
scope.$watch('model', function(newVal){
scope.value = newVal;
});
}
};
})
And the example usage:
<button-group ng-model="sortOrder" buttons="sortOptions"
button-class="'md-button my-other-class'"></button-group>
Where sortOptions would be an array of the form:
$scope.sortOptions = [
{ value: 'priority', text: 'Priority' },
{ value: 'duration', text: 'Call Duration' }
];

The attributes passed to directive in AngularJS change only into directive scope but not outside

I want to use a directive to customize my code.
I have created a button to switch isCollapsedUpload flag defined in the controller as: #scope.isCollapsedUpload=false.
When the user presses the button, the isCollapsedUpload turns to true or vice versa and the icon changes.
From the controller:
$scope.switcher = function (booleanExpr, trueValue, falseValue) {
return booleanExpr ? trueValue : falseValue;
}
$scope.isCollapsedUpload = false;
<button class="btn" ng-click="isCollapsedUpload = !isCollapsedUpload">
<span>Upload file</span>
<i class="{{ switcher( isCollapsedUpload, 'icon-chevron-right', 'icon-chevron-down' )}}"></i>
</button>
I wrote this directive:
feederliteModule.directive('collapseExtend', function() {
return {
restrict: 'E',
scope: { isCollapsed:'#collapseTarget' },
compile: function(element, attrs)
{
var htmlText =
'<button class="btn" ng-click="isCollapsed = !isCollapsed">'+
' <span>'+attrs.label+'</span>'+
' <i class="{{ switcher(isCollapsed, \'icon-chevron-right\', \'icon-chevron-down\' )}}"></i>'+
'</button>';
element.replaceWith(htmlText);
}
}
});
And now I can use it like:
<collapse-extend
collapse-target="isCollapsedUpload"
label="Upload file"
></collapse-extend>
It doesn't work. No icon changes. No errors,
isCollapsedUpload flag doesn't change. It changes only into directive
Did I miss something?
The reason the class doesn't change correctly is because you are not linking the template properly. This is easy to fix if you use the built in functionality:
var feederliteModule = angular.module('feederliteModule', []);
feederliteModule.directive('collapseExtend', [function() {
return {
restrict: 'E',
scope: {
isCollapsed:'=collapseTarget',
label: '#'
},
template: '<button class="btn" ng-click="isCollapsed = !isCollapsed">'+
'<span>{{ label }}</span>'+
'<i ng-class="{ \'icon-chevron-right\': isCollapsed, \'icon-chevron-down\': !isCollapsed }"></i>'+
'</button>'
}
}]);
feederliteModule.controller('test', ['$scope', function($scope) {
$scope.isCollapsedUpload = false;
}]);
To the best of my understanding, by replacing the parent element, you were removing the isolate scope this object was tied to without creating a new one on the button itself.
EDIT: See a complete working fiddle with multiple buttons
I suggest using a service instead of a controller to maintain your model data. This allows you better separation of concerns as your app gets more complex:
var feederliteModule = angular.module('feederliteModule', []);
feederliteModule.service('btnService', function(){
this.isCollapsedUpload = false;
this.isCollapsedSomething = false;
});
feederliteModule.controller('btnController', function($scope, btnService){
$scope.isCollapsedUpload = btnService.isCollapsedUpload;
$scope.isCollapsedSomething = btnService.isCollapsedSomething;
});
feederliteModule.directive('collapseExtend', function() {
return {
restrict: 'E',
scope: {
isCollapsed:'=collapseTarget',
label:'#'
},
replace: true,
link: function (scope, element, attrs){
scope.switcher = function (booleanExpr, trueValue, falseValue) {
return booleanExpr ? trueValue : falseValue;
};
scope.toggleCollapse = function() {
scope.isCollapsed = !scope.isCollapsed;
}
},
template: '<button class="btn" ng-click="toggleCollapse()">'+
'<span>{{label}}</span>'+
'<i ng-class="switcher(isCollapsed, \'icon-chevron-right\', \'icon-chevron-down\')"></i>'+
'</button>'
}
});
Also, notice that you must use '=' instead of '#' in order for isCollapsed to work as you expect. The answer above needs this as well.

Categories

Resources