I am trying to change "Collapse" text to "Expand" on click of h2 tag. at the same time I am applying "active" class to h2 tag. using following directive, which is working fine, but now I am clueless about how to change "collapse" text to Expand on h2 tag click
HTML
<h2 class="panel-title" toggleclass>My Tandem URL</h2>
<a class="collapse-arrow">Collapse</a>
js
.directive("toggleclass", function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
element.bind("click", function () {
element.toggleClass('active');
});
}
}
})
Can anyone plz help..
The
If you want to do DOM manipulation in your directive you could change the contents of the element by doing:
var link = element.find('a');
link.text(link.text() === 'Collapse' ? 'Expand' : 'Collapse')`.
Using html instead of text also works.
You have to move the link inside the h2 to have the directive see the link.
Another approach is having the link text changed via the scope, but then you need to have a template and bind the link text to the directive scope. Then you have to wrap both elements in a directive.
This may make the solution a bit too big for this simple use case...
Another suggestion on making up an directive: encapsulates html and behaivor inside: fiddle .
angular.module('myModule', [])
.directive('togglable', function () {
return {
restrict: 'EA',
template: '<div><h2 class="panel-title" ng-class="{ active: isCollapsed }" ng-click="toggle()" ng-transclude></h2><a class="collapse-arrow">{{ isCollapsed ? "Expand" : "Collapse"}}</a></div>',
transclude: true,
replace: true,
scope: false,
controller: function ($scope) {
$scope.isCollapsed = false;
$scope.toggle = function () {
$scope.isCollapsed = !$scope.isCollapsed;
};
}
};
});
Key features:
- no manual DOM manipulaton;
- uses transclude feature;
Such a directive is easy to use:
<togglable>My Tandem URL</togglable>
Depending on how your app works, maybe you can try this:
HTML:
<h2 class="panel-title" ng-class="{'active': expand}" toggleclass>My Tandem URL</h2>
<a class="collapse-arrow" ng-hide="expand">Collapse</a>
<a class="collapse-arrow" ng-show="expand">Expand</a>
JS:
angular.module('myApp', [])
.controller('myCTRL', function($scope) {
$scope.expand = false;
})
.directive("toggleclass", function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
element.bind('click', function() {
scope.$apply(function() {
scope.expand = !scope.expand;
});
});
}
}
});
http://jsfiddle.net/uqbc9asf/
Related
Let's say I have a "filterList" element. Inside is a list of <filter-item> angular directives. I want each <filter-item> to have a "delete" button inside of it. Clicking the delete button will remove the <filter-item> from the DOM.
How can I do this?
<div class="filterList">
<filter-item></filter-item>
<filter-item></filter-item>
<filter-item></filter-item>
</div>
FilterItem directive:
myApp.directive('filterItem', ['$http', function($http){
return {
scope: { },
replace: true,
link: function(scope, element, attrs){
scope.remove = function() {
// Hmm, I sure do wonder what goes here Mr Jones.
}
},
template: '<div>Hi mom! <button on-click="remove()">Remove</button></div>'
}
});
Inside of link you can do element.remove();.
Since AngularJS has jqLite, remove() removes the directive's element from the DOM. If your directive is not tied to any model, this is acceptable. Be careful manipulating the DOM like this when directives are rendered as a result of the model, since you haven't changed the model you can end up with strange effects (e.g. re-rendering).
(Self-answer but found a good post not on StackOverflow)
myApp.directive('filterItem', ['$http', function($http){
return {
scope: { },
replace: true,
link: function(scope, element, attrs){
scope.remove = function() {
element.remove();
}
},
template: '<div>Hi mom! <button on-click="remove()">Remove</button></div>'
}
});
method 1: i dont know if this is the right way to do it but you can use
<div class="filterList">
<filter-item></filter-item>
<filter-item></filter-item>
<filter-item></filter-item>
</div>
FilterItem directive:
myApp.directive('filterItem', ['$http', function($http){
return {
scope: {
hideDir:'=?'
},
replace: true,
link: function(scope, element, attrs){
scope.remove = function() {
// Hmm, I sure do wonder what goes here Mr Jones.
element.innerHTML='';
}
},
template: '<div>Hi mom! <button on-click="remove()">Remove</button></div>'
}
});
method 2: you can also try using ng-if and change variable inside directive
<div class="filterList" ng-if="!hideDir" hide-dir="hideDir">
<filter-item></filter-item>
<filter-item></filter-item>
<filter-item></filter-item>
</div>
FilterItem directive:
myApp.directive('filterItem', ['$http', function($http){
return {
scope: {
hideDir:'=?'
},
replace: true,
link: function(scope, element, attrs){
scope.remove = function() {
// Hmm, I sure do wonder what goes here Mr Jones.
scope.hideDir=true;
}
},
template: '<div>Hi mom! <button on-click="remove()">Remove</button></div>'
}
});
In this example, I have two AngularJS KendoDatePickers. The first one works perfectly, if you click on the button you open the calendar. The second one is within a directive that has the transclude attribute set to true. If you click on the second button, you get an error.
My understanding is that the scope of the transcluded portion inherits from the control scope, so this should work. Where am I wrong?
This is the plunk
HTML
<input kendo-date-picker="picker" />
<button ng-click="openPicker()">Open Date Picker</button>
<my-window>
<input kendo-date-picker="picker2" />
<button ng-click="openPicker2()">Open Date Picker 2</button>
</my-window>
Javascript
var app = angular.module("app", [ "kendo.directives" ]);
app.controller('MyCtrl', function($scope) {
$scope.openPicker = function () {
$scope.picker.open();
};
$scope.openPicker2 = function () {
$scope.picker2.open();
};
});
app.directive('myWindow', function() {
return {
transclude: true,
scope: {
someVar: '='
},
restrict: 'EA',
template: function(element, attrs) {
var html = '<div ng-transclude></div>';
return html;
}
};
});
There are two things about your code:
first: you create an isolatedScope so you do not have access to the controller scope inside the directive scope.
second: transcluded content get their own scope. One way to work around this is by not using transclude at all, like the example below:
return {
transclude: false,
restrict: 'EA',
template: function(element, attrs) {
var html = '<div>'+element.html()+'</div>';
return html;
}
or use the link function and manually transclude the element with the scope of the directive
I am trying to call a html page which is given in the templateUrl of my directive when I click a button, below is my code "hi" should be displayed when I click the "click me" button. Please suggest me how to do this.
sample.html:
<div ng-controller="MyController">
<button custom-click="">Click Me</button>
</div>
sample.js:
appRoot.directive('customClick', function() {
return {
link: function(scope, element, attrs) {
element.click(function(){
templateUrl:'/page.html';
});
}
}
});
Page.html:
<div><h4>HI</h4></div>
Update: The Snippet has been updated with getting the code from a URL
Adding onto the above answers:
appRoot.directive('customClick', function($http, $compile) {
return {
link: function(scope, element, attrs) {
element.click(function(){
$http.get("/page.html").then(function(resp){
$(element).html(resp.data);
var fnLink = $compile(element);
fnLink($scope);
});
});
}
}
});
P.S: Needs jQuery to run as using some functions like html() which can be bypassed if you dont want to include jQuery
I don't think that structure is possible, at all.
The easiest way would be to handle a show/hide type of functionality on the directive and have the template be there at all times.
For this you could use either ng-show, ng-hide or ng-if (and some others that I won't dig into).
base directive
appRoot.directive('customClick', function () {
return {
template: '<div><h5>HI</h5></div>',
link: function (scope, el, attrs) {
scope.active = false;
el.on('click', function () {
scope.$apply(function () {
scope.active = !scope.active;
});
});
}
}
});
ng-show
template: '<div ng-show="active"><h5>HI</h5></div>'
ng-hide
template: '<div ng-hide="!active"><h5>HI</h5></div>'
ng-if
template: '<div ng-if="active"><h5>HI</h5></div>'
Edit: If you are using templateUrl, simply put the ng-show/hide/if directive on the root element of the template being referenced, and this should work the same.
Oh, and here's a fiddle.
http://jsfiddle.net/ADukg/5426/
I have a sidebar with an absolutely positioned anchor that when clicked, should activate an AngularJS directive to open/close the sidebar.
This works fine when the anchor is outside of the aside element but for positioning reasons, I want it inside. However, inside the element, I see no evidence that the click is ever handled.
<div class="page-wrap">
<aside data-slide-toggle="smToggleSidebar" data-slide-toggle-duration="100">
<a ng-click="smToggleSidebar=!smToggleSidebar">〈</a>
// content here
</aside>
</div>
The directive is essentially:
.directive('slideToggle', function() {
return {
restrict: 'A',
scope:{
isOpen: "=slideToggle"
},
link: function(scope, element, attr) {
var slideDuration = parseInt(attr.slideToggleDuration, 10) || 200;
scope.$watch('isOpen', function(newVal,oldVal){
if(newVal !== oldVal){
$(element).stop().slideToggle(slideDuration);
}
});
}
};
})
I'm brand new to angular so I'm sure it's something I'm misunderstanding, likely with scope.
Angular directives create their own scope, so if you place the anchor tag inside of the directive it will belong to the directive's scope, not the parent. To allow the directive to have access to a parent function you have to pass that function to the directive by adding it to the scope:
scope:{
isOpen: "=slideToggle",
onToggle: '&'
}
And then manage the ng-click from inside of your directive. I would suggest something like this:
html
<div class="page-wrap">
<aside on-toggle="onToggle()" data-slide-toggle="smToggleSidebar" data-slide-toggle-duration="100">
<a ng-click="onToggle()">〈</a>
// content here
</aside>
</div>
directive
.directive('slideToggle', function() {
return {
restrict: 'A',
scope:{
isOpen: "=slideToggle",
onToggle: "&"
},
link: function(scope, element, attr) {
var slideDuration = parseInt(attr.slideToggleDuration, 10) || 200;
scope.$watch('isOpen', function(newVal,oldVal){
if(newVal !== oldVal){
$(element).stop().slideToggle(slideDuration);
}
});
}
};
});
parent controller
$scope.onToggle = function(){
$scope.smToggleSidebar =! $scope.smToggleSidebar;
};
In the following code, i change the object's property on clicking the 'tab' element, but the corresponding ngbind span is not getting updated. Do i have to call some function to update the view?
HTML:
<html ng-app="splx">
...
<body ng-controller="Application">
<span ng-bind="obj.val"></span>
<tabpanel selector="obj">
<div tab value="junk">junk</div>
<div tab value="super">super</div>
</tabpanel>
</body>
</html>
JS:
var cf = angular.module('splx', []);
function Application($scope) {
$scope.obj = {val: "something"};
}
cf.directive('tabpanel', function() {
return {
restrict: 'AE',
scope: {
selector: '='
},
controller: ['$scope', function($scope) {}]
};
});
cf.directive('tab', function() {
return {
require: '^tabpanel',
restrict: 'AE',
scope: true,
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$parent.selector.val = "newthing";
});
}
};
});
cf.directive('tab', function() {
return {
require: '^tabpanel',
restrict: 'AE',
scope: true,
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$apply(function () {
scope.$parent.selector.val = "newthing";
});
});
}
};
});
That works for me. Just missing a little scope.$apply in there.
Might want to have a look at https://coderwall.com/p/ngisma if you find yourself using/having trouble with '$apply already in progress'.
If you want to change the value to what you clicked on, I'd do something like this:
scope.$parent.selector.val = attrs.tab;
As opposed to:
scope.$parent.selector.val = "newthing";
And then you can change your markup to look like this:
<tabpanel selector="obj">
<div tab="junk">junk</div>
<div tab="super">super</div>
</tabpanel>
Hope that helps!
First problem: you are not binding your controller to your app.
You need cf.controller('Application', Application);.
Also you need ng-controller="Application" in HTML on a parent of that span and the tabpanel directive.
Second problem: after changing that scope variable in your click event you need to
scope.$apply() to let Angular know something changed and it needs to $digest it.
You can check out my version here.