I'm trying to toggle a class by setting a Boolean value with ng-class. Now while using a custom directive while clicking outside of my focusing element needs to change the Boolean to false.
app.factory('collapsed', function($rootScope){
var scope = $rootScope.$new(true);
scope.isCollapsed = true
return scope;
})
app.controller('optSideNav', optSideNav);
function optSideNav(collapsed) {
var self = this;
this.isCollapsed = collapsed.isCollapsed;
}
app.directive('offFocusing', ['$document', 'collapsed', function ($document, collapsed) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
console.log(element)
function elementClick(e) {
e.stopPropagation();
scope.isCollapsed = collapsed.isCollapsed;
}
element.on('click', elementClick);
// remove event handlers when directive is destroyed
scope.$on('$destroy', function () {
element.off('click', elementClick);
});
}
};
}]);
<!--controller area -->
<navng-class="sideNav.isCollapsed ? 'opt-sideNav' : 'opt-sideNavXl'" ng-controller="optSideNav as sideNav" ng-model="sideNav.isCollapsed">
<a ng-click="sideNav.isCollapsed = !sideNav.isCollapsed""><i class="material-icons md-16">menu</i></a>
</nav>
<!--directive area-->
<section class="opt-appArea" flex layout="column" off-focusing>
</section>
How do I asynchronously obtain the value changes while click element to controller?
Related
I'm new to AngularJs and learning now, in my current assignment I need to achieve multiple things on ng-click.
To hide and show some DOM elements based on the ng-click
Change the background of the element where the ng-click is applied on, I'm trying to acheive this using a directive.
Mark-up:
<div class="catFilter f6" ng-click="showSubCat = !showSubCat;toggleDropDown()">
Choose A Genre
</div>
<div class="inactive" ng-show="showSubCat" ng-click="hideSubCat = !hideSubCat" ng-hide="!hideSubCat">
</div>
<div class="cat-drop-menu-list" ng-show="showSubCat" ng-hide="!hideSubCat">
</div>
angular directive
retailApp.directive('toggleDropDown', function() {
return function(scope, element, attrs) {
$scope.clickingCallback = function() {
element.css({'background':'url("../images/down-arrow.png") no-repeat 225px 12px;'});
};
element.bind('click', $scope.clickingCallback);
}
});
Issues:
I'm not able to see the directive being applied, i.e., when I click on choose a genre, it is hiding and showing the other two divs, but not changing the back ground.
You can do this a couple ways, with bindings or directives:
http://jsfiddle.net/abjeex75/
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.show_sub_cat = false;
$scope.show = function () {
$scope.show_sub_cat = true;
}
$scope.hide = function () {
$scope.show_sub_cat = false;
}
});
app.directive('toggleBg', function () {
var directive = {
restrict: 'A',
link: link
}
return directive;
function link(scope, element, attr) {
element.on('click', function () {
element.toggleClass('red');
});
}
});
I have a global variable called RecruiterDashboard.IsColdList. It is a boolean that decides rather to run one directive or another. When the page is loaded the variable is made either true or false through normal scripting.
What I want to do is allow the user to toggle through an ng-click rather to show this other directive or not, so I made an ng-click and set RecruiterDashboard.IsColdList from 'false' to 'true'. Problem is this doesn't reload the angular page and fire the controllers off. How do make the page run through the controllers again?
This is what I have so far:
$scope.showColdList = function (projId) {
RecruiterDashboard.isColdList = true;
};
I want to point out that I am not using angular routing. I am using C# MVC.
My logic looks like so:
callPanelControllers.controller('callPanelController', function($scope) {
$scope.isColdList = RecruiterDashboard.isColdList;
});
callPanelControllers.controller('incomingCall', function ($scope) {
$scope.showColdList = function () {
RecruiterDashboard.isColdList = true;
};
});
<div ng-click="showColdList()" ng-controller="incomingCall"></div>
<div ng-controller="callPanelController">
<cold-list ng-show="isColdList"></cold-list>
</div>
i had developed sort of a hack, so that the entire content inside of a given element is reloaded on change of a certain variable.
csapp.directive("csReloadOn", ["$timeout", function ($timeout) {
var getTemplate = function () {
return '<div ng-if="doRefreshPageOnModeChange"><div ng-transclude=""></div></div>';
};
var linkFunction = function (scope, element, attrs) {
scope.doRefreshPageOnModeChange = true;
scope.$watch(attrs.csReloadOn, function (newVal, oldVal) {
if (newVal === oldVal) return;
scope.doRefreshPageOnModeChange = false;
$timeout(function () { scope.doRefreshPageOnModeChange = true; }, 100);
});
};
return {
restrict: 'A',
transclude: true,
template: getTemplate,
link: linkFunction
};
}]);
you can use it like
<div cs-reload-on="{{pagemode}}">
<!-- your html here-->
</div>
so it just removes and re-renders the complete content inside of the div, so everything is reinitialized etc etc.
I have a directive disable-ng-clicks and under certain conditions, I want to prevent all ng-clicks that are children of the directive. Here is some example markup:
<div disable-ng-clicks> <!-- directive -->
<a ng-click="someAction()"></a>
<div ng-controller="myController">
<a ng-click="anotherAction()"></a>
<a ng-click="moreActions()"></a>
</div>
</div>
If these were normal hyperlinks, I could do something like this in the link function:
function(scope, iElement, iAttrs) {
var ngClicks = angular.element(iElement[0].querySelectorAll('[ng-click]'));
ngClicks.on('click', function(event) {
if(trigger) { // a dynamic variable that triggers disabling the clicks
event.preventDefault();
}
});
}
But this does not work for ng-click directives. Is there another way to accomplish this?
Here is the best I could come up with. I created a new directive to replace ng-click:
directive('myClick', ['$parse', function($parse) {
return {
restrict: 'A',
compile: function($element, attrs) {
var fn = $parse(attrs.myClick);
return function (scope, element, attrs) {
var disabled = false;
scope.$on('disableClickEvents', function () {
disabled = true;
});
scope.$on('enableClickEvents', function () {
disabled = false;
});
element.on('click', function (event) {
if (!disabled) {
scope.$apply(function () {
fn(scope, { $event: event });
});
}
});
};
}
}
}]);
So in a different directive, I can have:
if (condition) {
scope.$broadcast('disableClickEvents');
}
and when I want to re-enable:
if (otherCondition) {
scope.$broadcast('enableClickEvents');
}
I don't like having to use a different directive for ng-click, but this is the best plan I could think of.
You are catching 'click' event on parent only because of JS events bubbling, so if you want to intercept it on all descendants, so your directive should get all descendants of current element, listen their 'click' event and prevent it if necessary.
This directive will iterate over all child elements, check to see if they have an ng-click attribute, and if they do, it will disable any registered click event handlers:
directive('disableNgClicks', function(){
return {
restrict: 'E',
link: function(scope, elem, attrs){
angular.forEach(elem.children(), function(childElem) {
if (childElem.outerHTML.indexOf("ng-click") > -1) {
angular.element(childElem).off('click');
}
});
}
}
})
Plunker demo
I know this is 2 years ago but I needed to do something similar and came up with a rather simple solution.
The object:
items: {
item1 : {
selected: 0,
other: 'stuff'
},
item2 : {
selected : 1,
other: 'stuff'
}
}
The HTML:
<div ng-repeat="item in items" ng-model="item.selected" ng-click="selectParent($event)">
<div ng-click="item.selected ? selectChild($event) : null">Child</div>
</div>
The functions:
$scope.selectParent = function($event) {
var itemScope = angular.element($event.currentTarget)scope().item;
itemScope.selected = !itemScope.selected;
}
$scope.selectChild = function($event) {
$event.stopPropagation;
console.log('I only get triggered if parent item is selected');
}
This is a pretty raw example of what I did. You should probably be using a directive that gives you $scope rather than angular.element($event.currentTarget).scope... either way the simplistic inline if logic is what I was really getting at. You can call a function or not based on some value.
I am trying to implement a dropdown mega-menu using Angular, where if the user clicks on Link 1, then the content for Link 1 should appear. My directives are as followed:
mobile-menu which acts as a controller and keeping track of the links and menu items states
menu-link, the actual link that user clicks on to open/close the menu items
menu-item, the menu item that should show/hide based on the scope.test value
Here is my AngularJS code:
angular.module("myApp", [])
.controller("testCtrl", function ($scope) {})
.directive("mobileMenu", function () {
return {
controller: function ($scope) {
this.menuLinks = [];
this.menuItems = [];
this.addMenuLink = function (l) {
this.menuLinks.push(l);
};
this.addMenuItem = function (m) {
this.menuItems.push(m);
};
// Function to close all other menu items if they are open.
// This is because only one menu item can be active at a time
this.closeOthers = function (selectedMenuLink) {
angular.forEach(this.menuLinks, function (l) {
if (l !== selectedMenuLink) {
l.selected = false;
}
});
angular.forEach(this.menuItems, function (m) {
if (selectedMenuLink.target == m.menuId) {
m.test = true;
} else {
m.test = false;
}
});
};
}
};
}).directive("menuLink", function () {
return {
require: "^mobileMenu",
scope: {},
link: function(scope, element, attrs, menuController) {
scope.selected = false;
menuController.addMenuLink(scope);
scope.$watch('selected', function(newValue, oldValue) {
if (oldValue === newValue) {return};
if (newValue) {
scope.target = angular.element(element[0].children[0]).attr("data-menu");
menuController.closeOthers(scope);
}
});
}
};
}).directive("menuItem", function () {
return {
require: "^mobileMenu",
scope: true,
link: function (scope, element, attrs, menuController) {
scope.test = false;
scope.menuId = attrs.id;
menuController.addMenuItem(scope);
scope.$watch('test', watchLink);
scope.$watch(attrs.collapse, watchLink);
scope.$watch(function () {
return scope.test;
}, watchLink);
var watchLink = function (newValue, oldValue) {
// Initializing for the first time, do nothing
if (newValue === oldValue) return;
// If the collapse attribute has a true value, collapse this element
if (newValue) {
collapse();
} else {
expand();
}
};
// Helper function to collapse the element
var collapse = function () {
element.css({
height: "0px"
});
};
// Helper function to show the element
var expand = function () {
element.css({
height: "200px"
});
};
}
};
});
And here is my HTML code:
<div ng-app="myApp" ng-controller="testCtrl">
<div mobile-menu>
<ul>
<li menu-link>
Link 1
</li>
<li menu-link>
Link 2
</li>
</ul>
<div id="menu0" ng-class="{'expanded' : test, 'collapsed' : !test}" menu-item collapse="!test">
<p class="text">First Menu</p>
</div>
<div id="menu1" ng-class="{'expanded' : test, 'collapsed' : !test}" menu-item collapse="!test">
<p class="promo-text">Second Menu</p>
</div>
</div>
I have an issue where if menu-link #1 is clicked, the corresponding menu-item #1's scope.test value should be updated and its' scope watch should be triggered, but it does not. If the scope watch triggered the watchLink function, then I would expect menu-item #1 would have a height of 200px.
I have also attached a jsfiddle example:
http://jsfiddle.net/EmwBP/28/
If you look at the browser console tool, the corresponding menu-item ng-class is always updated based on its scope.test value. However, even with my 3 different scope watchers set up, none of them were triggered. I am also using the ng-class directive just to show that the scope.test value does get updated and will be removed in the final implementation.
Normally, I would have put the menu-item directives as a child of menu-links, but I have a requirement where I have to put the menu-items as it is right now to achieve the slide-down effect of pushing elements below it down.
Many thanks in advance for your advices and assistance
You create a new isolated scope in menuLink by using scope: {}. So whatever scope variables you set inside that scope (like selected) are only available within that scope, and will not propagate to sibling or parent scopes.
What you need to do is de-isolate the menuLink scope by using scope: true.
That is not the problem. The problem is that watchLink is used before you define it. Fix where watchLink is triggered: http://jsfiddle.net/EmwBP/31/
I am using bs-popover to display my contents on click(as a menu) in angularjs. But I need to hide this popover-menu when I click somewhere in the browser window. I want it to be dismissed on that type of event. How can I do that?
You need to write directive for this.
yourApp.directive('bndocumentclick',
function($document,$rootScope,$timeout) {
return {
restrict: 'EA',
link : function(scope, element, attrs) {
$document.on("click", function(ev) {
// Do stuff here to remove your popover.
}
}
}
});
HTML
<body bndocumentclick>
And
<div bs-popover ng-click="$event.stopPropagation()">
You need to use because you would not like to close your popover whenever user clicks inside popover.
The solution provied by #Jay Shukla doesn't work.
The "$event.stopPropagation()" on the element that triggers the popover doesn't stops it from closing when you make a click inside the popover.. if you have some interaction inside your popover this will be a problem.
This works:
angular.module('yourApp')
.directive('closePopovers', function ($document, $rootScope, $timeout) {
return {
restrict: 'EA',
link: function (scope, element, attrs) {
$document.on('click', function (ev) {
var targetElem = angular.element(ev.target);
if (targetElem.data('toggle') !== 'popover'
&& targetElem.parents('[data-toggle="popover"]').length === 0
&& targetElem.parents('.popover').length === 0) {
$('.popover').each(function () {
var $this = $(this);
var scope = $this.scope();
scope.$apply(function () {
scope.$hide();
});
}
);
}
});
}
};
});
On your body:
On your element that triggers the popover:
<button data-toggle="popover" [other data elements here] bs-popover>Toggle popover</button>