Angular directive to swap ng-click function - angularjs

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>

Related

AngularJS parent div directive wait for children div stacked directives to complete

I have a portion of html that is like this
<div my-custom-security-directive>
<button ng-if={{some constraints}} ng-click={{logic}}>cancel</button>
<button ng-disabled={{some logic}} ng-click={{some logic}}>submit<button>
</div>
My custom security directive does dom manipulation to the buttons to show/hide them via css. The issue I am having is with timing and perhaps directive precedence? When all the code is finished executing I only see the submit button and not the cancel button. I believe this is because of the ng-if and I attempted to set the priority of my custom directive to a negative number to run last but I think that is only for stacked directives on the same element? I only have a link function defined in my directive which my understanding is that is the same as a post link function and should run once the children complete? Ultimately, my goal is to run my directive 'last' so that I can show both buttons if all the logic in the directive passes.
The shell of my directive :
angular.module('myModule')
.directive("myCustomSecurityDirective", function(a,b) {
//
return {
restrict: "A",
priority: -1,
link: function(scope, element, attrs, ctrl) {
//custom security role/perm logic using injected services a&b etc
if (userHasPermission ) {
element.find('input, textarea, button').addClass('my-show-css');
}
}
};
});
I did recently /today put that priority on the directive but I don't think it does anything in this particular scenario.
Even if my-custom-security-directive is able to attach a CSS class to the button and hide or show it, the button has its own ng-if condition. This means it's possible that the button could be destroyed and recreated later, and it wouldn't have the CSS class anymore. If the button uses ng-show instead of ng-if you may have more control, since the button would become hidden but remain in the DOM.
But I think my-custom-security-directive might want to have more control. You can use transclusion so that my-custom-security-directive acts as a container for each set of elements which should be destroyed or created based on userHasPermission.
Directive:
.directive('myCustomSecurityDirective', function () {
return {
transclude: true,
link: function (scope) {
scope.userHasPermission = true;
},
template: '<div ng-if="userHasPermission" ng-transclude></div>'
}
});
HTML:
<div my-custom-security-directive>
<button ng-if="...">cancel</button>
<button ng-disabled="...">submit</button>
</div>

Why does my Angular directive behave like this?

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?

Make a div focused and everything else dark using AngularJS

I need this widget for Angular:
https://github.com/zzarcon/focusable
I need to be able to focus a div, make everything outside it dark and not able to click outside. I need to draw my user attention only to the div.
Is that possible?
The best way would be to create a directive which uses the jQuery lib you suggested:
myApp.directive('myFocusableDirective', function() {
return {
link: function(scope, elem) {
Focusable.setFocus(elem);
}
};
});
<div my-focusable-directive>
I AM FOCUSABLE!
</div>

AngularJS directive remove class in parent element

I am using the following code to add / remove class "checked" to the radio input parent. It works perfectly when I use JQuery selector inside the directive but fails when I try to use the directive element, can someone please check my code and tell me why it is not working with element and how I can possibly add/ remove class checked to the radio input parent while using element instead of the jquery selectors? Thanks
.directive('disInpDir', function() {
return {
restrict: 'A',
scope: {
inpflag: '='
},
link: function(scope, element, attrs) {
element.bind('click', function(){
//This code will not work
if(element.parent().hasClass("checked")){
scope.$apply(function(){
element.parent().removeClass("checked");
element.parent().addClass("checked");
});
}else{
scope.$apply(function(){
element.parent().addClass("checked");
});
}
//This code works perfectly
$('input:not(:checked)').parent().removeClass("checked");
$('input:checked').parent().addClass("checked");
});
}
};
});
HTML:
<div class="inpwrap" for="image1">
<input type="radio" id="image1" name="radio1" value="" inpflag="imageLoaded" dis-inp-dir/>
</div>
<div class="inpwrap" for="image2">
<input type="radio" id="image2" name="radio1" value="" inpflag="imageLoaded" dis-inp-dir/>
</div>
Your code actually works for me in Plnkr (more or less):
http://plnkr.co/edit/vJJRYQQxH7u2bKSc27UA?p=preview
When you run this, the 'checked' class gets correctly added to the parent DIVs using only the first code you included. (I commented out the jQuery mechanism - I didn't add jQuery to this page, as a test.)
However, I think what you're trying to accomplish isn't working out because you're only capturing click events. The radio button that loses its checked attribute doesn't get a click event, only the next one does. In jQuery your selector is really broad - you're hitting every radio button, so it does what you want. But since you only trap click on the radio button that receives the click, it doesn't do what you want using the other pattern. checked gets added, but never removed.
A more Angular-ish pattern would be something like this:
http://plnkr.co/edit/HN7tLxkRA0jUL5GPjk5V?p=preview
link: function($scope) {
$scope.checked = false;
$scope.$watch('currentValue', function() {
$scope.checked = ($scope.currentValue === $scope.imgNumber);
});
$scope.setValue = function() {
$scope.currentValue = $scope.imgNumber;
};
}
What you see here lets Angular do all the dirty work, which is kind of the point. You can actually go a lot further than this - you could probably cut half the code out and do it all with expressions. The point is that in Angular, you really want to focus on the DATA (the model). You wire all of your behaviors and events up (controller) to things that manipulate that data, and then wire up all your DOM styles, classes, templates (view), etc. up to conditionals against that same data. And that is the point of MVC!

Close AngularStrap popover

When I click a button, it appears a popover which can be closed if you click the button inside the popover. However, if you click another button to open a popover, you will see two popovers at the same time and I want to keep just one.
I have tried with the trigger 'focus', but it closes the popover if I click in the textarea inside the popover, I expected that this trigger was called when you click outside of the popover.
Any idea for this? Can the methods $hide, $show be called from the controller?
Try to add ng-click="$hide()" on the dismissable element of the popover. I.E.:
<a class="btn btn-primary" ng-click="$hide()">Close</a>
It's not included in the documentation but it works for me, iff you use data-template for popover content, haven't tried with other opened popover yet.
This should be an old question, in latest version of angular-strap, a new attribute could be used in this case:
auto-close='1'
OK, I am pretty sure this is a terrible hack, but here goes. Assuming your popover templates all use the popover class (if you aren't using a custom template with the data-template attribute, they should), and they're siblings of the button that triggers them (if you haven't mucked with the container attribute, they are), you can apply the following directive to your popover buttons. Note: this assumes that the parent elements of your popovers and popover buttons have unique ids.
angular.module('yourApp.directives', []).directive('rmPopovers',
function($document,$rootScope,$timeout,$popover) {
return {
restrict: 'EA',
link : function(scope, element, attrs) {
var $element = $(element);
$element.click(function() {
$('.popover').each(function(){
var $this = $(this);
if($this.parent().attr('id') != $element.parent().attr('id'))
{
$this.scope().$hide();
}
}
);
});
}
}
}
);
And then
<button type="button" bs-popover rm-popovers [your data attribs here]>Button!</button
There is an issue in the angular-strap github project which asks exactly the feature you want.
Nevertheless, at the moment I'm writing this answer, it's still open.
trigger : 'focus' ,
worked for me on the same issue

Resources