I have a submit button in my form which is enabled if the form is valid, otherwise, it will be on disable state. I want to add a popup message on hover over the disabled button not enabled button. How to add it dynamically in angularjs?
Bootstrap has a decent tooltip, if it's not to your liking, feel free to use another, tho when writing a directive it will be different.
I prefer this over writing your own tooltip module since this way you have a tested and robust way to display tooltips anywhere.
Then you are gonna want to wrap it as a directive to work in AngularJS like:
app.directive('tooltip', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
element.hover(function(){
// on mouseenter
element.tooltip('show');
}, function(){
// on mouseleave
element.tooltip('hide');
});
}
};
});
Now you have a tooltip that is styled and should work when attached to any element. Attach it to the said button:
<button tooltip="myTooltip">I am a button</button>
NOTE: If disabled prevents it from showing (didn't test that) then simply wrap that button in a <span></span> and bind tooltip to that span instead since span is not disabled.
<span tooltip="myTooltip"><button>I am a button</button></span>
In any case, either you bind directly to button or span around it, don't forget in the controller for that view, set the tooltip text:
$scope.myTooltip = 'Tooltip text';
Related
How can I prevent default on child element?
I have content element:
<div id="content" class="full" ng-swipe-right="openSwipeNav(allowSwipe); swiping = true" ng-swipe-left="closeSwipeNav(allowSwipe); swiping = true" ng-mouseup="closeNav($event)" ng-click="swiping=false;">
Inside this #content is:
<input type="range">
But it's not working. I can move range slider just a little bit and of course it trigger opening navigation. When I remove ng-swipe-right & ng-swipe-left it works ok.
I tried this solution: link
But it still didn't work for range input...
Any suggestions?
There can be couple of different ways in which you can achieve this.
Keep in mind that the same swipe event is being caught by both the slider and the content. Also, because the slider is a child of content, when you slide it, the event is first received by the slider and then bubbled up to the content. So you can stop the event's propagation inside the handler for slider's swipe.
Or, if you want to be more verbose about it, catch the event in the content's swipe-left handler function, check it's target and confirm that it doesn't contain the slider element as it's target.
Maybe create a directive called something like 'disableSwipe' which bind to touch events and prevents their propagation, to the range element.
myApp.directive('disableSwipe', function(){
return {
link: function (scope, element, attrs) {
var disableSwipeListener = function(event) {
console.log('mm')
event.stopPropagation();
}
$(element).on("mousedown", disableSwipeListener);
$(element).on("mousemove", disableSwipeListener);
$(element).on("mouseup", disableSwipeListener);
}
}
})
Heres a quick and dirty fiddle to get you started: http://jsfiddle.net/yve2rLkr/25/
I use a tooltip directive from Angular UI Bootstrap inside one of my own directives. I want to manually toggle tooltip's visibility using tooltip-is-open attribute that Bootstrap Tooltip provides. Angular UI documentation states:
tooltip-is-open <WATCHER ICON> (Default: false) - Whether to show the tooltip.
So I assume it watches on the attribute's value. And so I want to bind a scope variable tooltipIsOpen to the attribute, hoping that changing tooltipIsOpen value in my directive's link function would toggle the tooltip visibility.
The behaviour that I get is weird.
If I bind tooltipIsOpen value as an expression: tooltip-is-open="{{ tooltipIsOpen }}", the tooltip doesn't appear at all, though I see that the DOM is updated properly (tooltip-is-open="false switches to tooltip-is-open="true" and back again to false) in reaction to mouse events.
If I bind tooltipIsOpen value as a variable: tooltip-is-open="tooltipIsOpen", the tooltip apears properly after the first mouse event, but then doesn't react to any further events, staying displayed all of the time with no way to hide it.
The working solution I found is bind tooltipIsOpen as a function call :scope.tooltipIsOpenFun = function() { return tooltipIsOpen; } and tooltip-is-open="tooltipIsOpenFun()", or as an object property: tooltip-is-open="tooltip.isOpen". Only then the tooltip works fine - shows and hides all of the time.
I suspect it's related to how AngularJS watchers work, also to the difference in how JavaScript primitives and objects are treated when assigning the values. Still I can't understand it.
Question
Can somebody explain to me step by step why the solution 1 doesn't work and 2 works only once?
Directive template (for method 2)
<div uib-tooltip="Tooltip message"
tooltip-is-open="tooltipIsOpened">
...
</div>
Directive link function
module(...).directive(..., function() {
return {
link: function(scope, elem, attr) {
/* Stop further propagation of event */
function catchEvent(event) {
event.stopPropagation();
};
function showTooltip(event) {
scope.$apply(function() {
scope.tooltipIsOpened = true;
/* Hide tooltip on any click outside the select */
angular.element(document).on('click', hideTooltip);
elem.on('click', catchEvent);
});
};
function hideTooltip() {
scope.$apply(function() {
scope.tooltipIsOpened = false;
/* Remove previously attached handlers */
angular.element(document).off('click', hideTooltip);
elem.off('click', catchEvent);
});
};
scope.tooltipIsOpened = false;
elem.on('mouseenter', showTooltip);
elem.on('mouseleave', hideTooltip);
});
1st way is incorrect because this directive expects variable name, not variable value. So correct:
<div uib-tooltip="Tooltip message" tooltip-is-open="xxx">
Or correct (you pass {{}} but then you pass not value of variable but value of variable that stores variable name):
<div ng-init="yyy='xxx'">
<div uib-tooltip="Tooltip message" tooltip-is-open="{{yyy}}">
2nd way astually works somehow:
http://plnkr.co/edit/OD07Oj2A0tfj60q2QKHe?p=preview
But it is very strange - how user supposed to click outside element without triggering mouseleave event?
The thing is that you do not need all this, just:
<button uib-tooltip="Tooltip message" trigger="mouseenter"
tooltip-is-open="tooltipIsOpened">
hello
</button>
works fine.
3rd way: if you see that 'open' does not work, but 'smth.open' works that usually means you faced 'dot issue' - you can not change parent scope variable from child scope directly, but you can change variable properties. There is a lot of examples and explanations of this issue.
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>
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
I've been looking around and trying out different things but can't figure it out. Is it possible to hide an angular-ui tooltip with a certain event?
What I want to do is to show a tooltip when someone hovers over a div and close it when a users clicks on it because I will show another popup. I tried it with custom trigger events but can't seem to get it working. I made this:
<div ng-app="someApp" ng-controller="MainCtrl" class="likes" tooltip="show favorites" tooltip-trigger="{{{true: 'mouseenter', false: 'hideonclick'}[showTooltip]}}" ng-click="doSomething()">{{likes}}</div>
var app = angular.module('someApp', ['ui.bootstrap']);
app.config(['$tooltipProvider', function($tooltipProvider){
$tooltipProvider.setTriggers({
'mouseenter': 'mouseleave',
'click': 'click',
'focus': 'blur',
'hideonclick': 'click'
});
}]);
app.controller('MainCtrl', function ($scope) {
$scope.showTooltip = true;
$scope.likes = 999;
$scope.doSomething = function(){
//hide the tooltip
$scope.showTooltip = false;
};
})
http://jsfiddle.net/3ywMd/
The tooltip has to close on first click and not the 2nd. Any idea how to close the tooltip if user clicks on div?
I tried #shidhin-cr's suggestion of setting $scope.tt_isOpen = false but it had the rather significant issue that, while the tooltip does fade out, it is still present in the DOM (and handling pointer events!). So even though they can't see it, the tooltip can prevent users from interacting with content that was previously behind the tooltip.
A better way that I found was to simply trigger the event used as tooltip-trigger on the tooltip target. So, for example, if you've got a button that's a tooltip target, and triggers on click...
<button id="myButton"
tooltip="hi"
tooltip-trigger="click">
</button>
Then in your JavaScript, at any point, you can trigger the 'click' event to dismiss your tooltip. Make sure that the tooltip is actually open before you trigger the event.
// ... meanwhile, in JavaScript land, in your custom event handler...
if (angular.element('#myButton').scope().tt_isOpen) {
angular.element('#myButton').trigger('click');
}
Since this triggers the actual internals of AngularUI's Tooltip directive, you don't have the nasty side-effects of the previous solution.
Basically you cannot play with the tooltip-trigger to make this work. After digging through the ToolTip directive code, I found that the ToolTip attribute exposes a scope attribute called tt_isOpen.
So in your ng-click function, if you set this attribute to false, that will make the tooltip hide.
See the updated demo here
http://jsfiddle.net/3ywMd/10/
Like this
app.controller('MainCtrl', function ($scope) {
$scope.likes = 999;
$scope.doSomething = function(){
//hide the tooltip
$scope.tt_isOpen = false;
};
})
Michael's solution got me 90% of the way there but when I executed the code, angular responded with "$digest already in progress". I simply wrapped the trigger in a timeout. Probably not the best solution, but required minimal code
// ... meanwhile, in JavaScript land, in your custom event handler...
if (angular.element('#myButton').scope().tt_isOpen) {
$timeout( function(){
angular.element('#myButton').trigger('click');
}, 100);
}
For future reference, the accepted answer angular.element('.yourTooltip').scope().tt_isOpen will not work in new versions as tooltip has been made unobservable. Therefore, the entire tootlip is removed from DOM. Simple solution is to just check if tooltip is present in DOM or not.
Borrowing from #liteflier's answer,
// ... meanwhile, in JavaScript land, in your custom event handler...
if (angular.element('.yourTooltip').length) { //if element is present in DOM
setTimeout( function(){
//Trigger click on tooltip container
angular.element('.yourTooltipParent').trigger('click');
}, 100);
}