Why does my Angular directive behave like this? - angularjs

I want to implement a directive that changes some HTML colors in a view when a certain {{valueFromServer}} changes. Currently, I've tried the following:
View:
<span class="glyphicon glyphicon-thumbs-up voteColorer" ng-click="upvote(post)" votes="{{valueFromServer}}"></span>
<span class="glyphicon glyphicon-thumbs-down voteColorer" ng-click="downvote(post)" votes="{{valueFromServer}}"></span>
Directive:
app.directive('voteColorer', function () {
return {
restrict : 'C',
link: function(scope, elem, attrs) {
elem.bind("click" , function(e){
attrs.$observe('votes', function() {
console.log(elem); // for testing purposes
// would like to do color change operations here
});
});
}
}
});
When I click either of the glyphicon spans and valueFromServer changes I'd expect the directive to log into the console the span element I just clicked.
What it actually does is it first logs the span I click. On my second click it logs the span I clicked AND the span I clicked before that. On my third click it logs the span I just clicked, the span I clicked before that and the span I clicked before that etc. So on e.g. my fifth click the directive prints five console.log elements.
Why does it do this and how do I get rid of this behavior?

Related

How to use a different combination of triggers for uib-popover?

The official documentation at :https://angular-ui.github.io/bootstrap/#/popover says that the following trigger combos can be passed as param to the popover-trigger attribute :
mouseenter: mouseleave
click: click
outsideClick: outsideClick
focus: blur
none
I want to use a combination of
mouseenter: outsideClick
How to achieve this without using the popover-is-open attribute?
You can't, the docs state
The outsideClick trigger will cause the popover to toggle on click, and hide when anything else is clicked.
"anything else" includes the element itself, so toggeling the element using outsideClick on or off and will interfere with the natural behavior of other triggers.
for example if state your triggers like so popover-trigger="mouseleave outsideClick"
, the trigger mouseleave will hide the popover instead of showing it if you have already clicked the element, otherwise it will just show it on leave. (plunk).
If you can hack it using popover-is-open then continue doing so, if it bothers you too much you can always request a feature.
popover-trigger="mouseenter outsideClick" for the uib-popover directive does not seem to work as one would think.
Initially, I thought it meant the following:
On mouse enter show the popover
On mouse leave hide the popover
On click keep popover open in an active state
On outside click close popover if it is in an active state
Since it does not I needed a manual approach, the following is stated in the documentation.
For any non-supported value, the trigger will be used to both show and hide the popover. Using the 'none' trigger will disable the internal trigger(s), one can then use the popover-is-open attribute exclusively to show and hide the popover.
So I created some HTML like:
<span class="glyphicon glyphicon-info-sign"
ng-class="{'text-primary' : isInfoPopoverClicked}"
ng-click="toggleInfoPopoverClicked()"
ng-mouseenter="enterInfoPopover()"
ng-mouseleave="leaveInfoPopover()"
custom-click-outside="closeInfoPopover()"
uib-popover-template="'info.html'"
popover-trigger="'none'"
popover-is-open="isInfoPopoverOpen()"
popover-placement="auto top"
popover-append-to-body="true" >
</span>
The JS in the controller:
// Toggle popover's clicked active state
$scope.toggleInfoPopoverClicked = function() {
$scope.isInfoPopoverClicked = !$scope.isInfoPopoverClicked;
};
// Close the popover, used for outside click and close action inside the template
$scope.closeInfoPopover = function() {
delete $scope.isInfoPopoverClicked;
};
// On mouse enter, show the popover
$scope.enterInfoPopover = function() {
$scope.isInfoPopoverMouseEnter = true;
};
// On mouse leave, close the popover.
// If clicked active state is false set to undefined.
// This supports when the user clicks the icon to close,
// that mouse enter does not immediately display the popover again.
$scope.leaveInfoPopover = function() {
$scope.isInfoPopoverMouseEnter = false;
if(false === $scope.isInfoPopoverClicked) {
delete $scope.isInfoPopoverClicked;
}
};
// Expression used in the popover-is-open attribute
$scope.isInfoPopoverOpen = function() {
if($scope.isInfoPopoverClicked) {
return true;
} else if(false === $scope.isInfoPopoverClicked){
return false;
}
return $scope.isInfoPopoverMouseEnter;
};
The template for the uib-popover-template I used:
<div custom-stop-event="click" class="pull-right">
<span ng-click="closeInfoPopover()" class="glyphicon glyphicon-remove"></span>
<section>{{info}}</section>
</div>
Now the trickier part was that this solution required me to create two more directives.
One to close the popover when clicking outside the element.
Another to stop the click event fired inside the pop-up. Preventing it from closing the popover.
The custom-click-outside directive:
angular.module('LSPApp').directive('customClickOutside', ['$document', function ($document) {
return {
restrict: 'A',
scope: {
clickOutside: '&customClickOutside'
},
link: function (scope, element) {
var handler = function (event) {
if (element !== event.target && !element[0].contains(event.target)) {
scope.$applyAsync(function () {
scope.clickOutside();
});
}
};
// Might not work on elements that stop events from bubbling up
$document.on('click', handler);
// Clean up event so it does not keep firing after leaving scope
scope.$on('$destroy', function() {
$document.off('click', handler);
});
}
};
}]);
The custom-stop-event directive called from the template's HTML:
angular.module('LSPApp').directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.on(attr.stopEvent, function (e) {
e.stopPropagation();
});
}
};
});
Hopefully, this helps someone, my final solution had all this encapsulated in it's own directive to promote reuse.

Angular directive to swap ng-click function

I have an app where a person can build lists of 'favorites.' On the page for a particular item there is a button that you can click to add the item to your list of favorites.
What I would like to happen is: Click the button, item is added to favorites, now the button is a different color and if you were to click it again it would remove the item from your favorites.
I think it would be best to build a directive to handle this but I am completely lost.
In my html I have:
<md-button class="md-icon-button" aria-label="Favorite" ng-click="addFavorite(item.id)">
<md-icon md-svg-icon="heart"></md-icon></md-button>
My addFavorite and deleteFavorite functions work correctly, but I can't figure out how to toggle which one happens and how to update that after the request fires.
So lets address a few different things.
Most likely this is going to be a div styled to look like a button to achieve the effect you want. You could go the easy route and use http://getbootstrap.com/css/?#buttons or something else. Styling buttons is just not going to be the best cross browser support.
You could do this entirely with ng-class in a directive, some example code below. Assuming you are using bootstrap. This just becomes what class do you want to change it to kinda thing. Below I have used an icon but the principal is the same.
Directive
.directive('toggleClass', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
if(element.attr("class") == "glyphicon glyphicon-pencil") {
element.removeClass("glyphicon glyphicon-pencil");
element.addClass(attrs.toggleClass);
addFavorite(element.id);
} else {
element.removeClass("glyphicon glyphicon-ok");
element.addClass("glyphicon glyphicon-pencil");
removeFavorite(element.id);
}
});
}
};
});
Template
<i class="glyphicon glyphicon-pencil" toggle-class="glyphicon glyphicon-ok"></i>

ionic-modal and img directive don't play nice

I am using ionic to build an app. I've written a global img directive that displays an alert box whenever an img is loaded. The problem I am facing is this directive is called for all views, but if I display an image inside an ionic-modal, the directive is not called.
My directive code is:
.directive('img', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
console.log ("********** IMG DIRECTIVE ");
}
}
})
I've added a codepen here so you can see what is going on. It's a fork from a standard ionic modal example, so has some redundant code.
http://codepen.io/asker/pen/YXXyZj?editors=101
a) Click on the link at the bottom of the home page that says "CLICK ON IMAGE TO SEE DIRECTIVE" and you will see just before the image loads, the directive alert displays
b) Click on "Sign In" on home page and in the next page, click "Open Modal" button - and you will see the image show up but my directive not called
Thanks
I think it is because your img directive is called when you click "Sign In", the home tab loaded the modal.html in your HomeTabCtrl before you click "Open Modal".
You can make changes like following:
Changes to home.html: use ng-click="openModal()" instead of ng-click="modal.show()"
Changes to HomeTabCtrl:
$scope.openModal = function(){
$ionicModal.fromTemplateUrl('modal.html', function($ionicModal) {
$ionicModal.show();
}, {
// Use our scope for the scope of the modal to keep it simple
scope: $scope,
// The animation we want to use for the modal entrance
animation: 'slide-in-up'
}); }

Custom directive to detect out side the button click

I need to write a custom directive to close (or hide) the button when click out side the button (empty area of the DOM). In other words anywhere except within the button ? This behaviour should be applied only for this button when I apply the custom directive.Any guide would be highly appreciated.
<button class="btn btn-primary" ng-click="CreateUpdate()">Submit</button>
app.directive('MyDirective', function() {
//content
});
Within the button directive you can inject $document. Then wire up on click on the document.
<button class="myButton btn btn-primary" ng--lick="CreateUpdate()">Submit</button>
mainModule.directive("myButton", ['$document',
function ($document) {
return {
restrict: "E",
link: function (scope, element, attrs) {
$document.bind('click', function(event){
//Get the element clicked
var clickedElement = angular.element(event.target);
// If the clickedElement is not same as button (say, the clicked element class is different from button class) then close the button.
}
}
})

Click event gets executed even after window.confirm returns false in angular confirmation dialog

Based on the example given here I have written a directive to display a confirmation dialog in Angular when a button is clicked. The problem is even after the user clicks on the cancel button on the confirmation dialog, the ng-click action gets triggered.
This is the directive:
app.directive('ngConfirmClick',function(){
return {
link: function(scope, element, attr) {
var msg = attr.ngConfirmClick;
var clickAction = attr.ngClick;
attr.ngClick = "";
element.bind('click', function(event) {
if(window.confirm(msg)){
scope.$eval(clickAction);
}
});
}
}
});
This is the section of my index.html that has the ng-confirm-click directives:
<input class="delete" type="button" value="" ng-click="delete(item._id)"
ng-confirm-click="Are you sure you want to delete?">
I tried setting a priority of -1 for the ng-confirm-click directive and that did not help either.
When I was debugging the code on firebug, I found that window.confirm DOES return false when the user clicks on Cancel, so I am not sure why the ng-click (clickAction in the directive code above) is getting executed.
Is there a way to stop propagating the click action if window.confirm returns false?
Any help would be appreciated.

Resources