How can I trigger an angularjs event handler from another handler? - javascript

I'm using AngularJS 1.3.0-beta.2, jQuery 2.1.0, Angular UI 0.11, and my custom version of Angular UI's Tooltip widget, and I want buttons within my tooltip to close the tooltip when clicked.
Plunkr
The key part is at crud_tooltip.js:372:
scope.closePopup = function() {
var trigger = element.prev();
if (scope.mode === 'timeout') {
$timeout(function() {
trigger.triggerHandler('click');
});
}
else {
trigger.triggerHandler('click');
}
};
The version using $timeout works, but there's a noticeable delay between clicking the button and seeing the popup close.
The version not using $timeout gives an error: [$rootScope:inprog] $apply already in progress. It then closes the popup anyway... I'm not sure why.
How can I modify closePopup (or the ng-click that calls it) to make the tooltip close immediately when the user clicks the button within the tooltip?
Note: I adapted my custom_tooltip.js code from Angular UI's tooltip code, using this Plunker as a guideline. I've also tried directly changing the tt_isOpen value and defining a new crudtooltip-toggle attribute, both with very limited success.

Maybe I'm missing the point, but your code seems incredibly complicated and convoluted for such simply functionality. In any case, the delay is actually due to a $timeout which is waiting for some animation to finish. The timeout is triggered because scope.tt_animation evaluates to truthy. Simply changing the timeout to 0 at line 258 of crud_tooltip.js fixes the issue. See this plunk
Here's the problem area:
if ( scope.tt_animation ) {
transitionTimeout = $timeout(removeTooltip, 500);
} else {
removeTooltip();
}

You're looking at the wrong thing.
That delay is coming from elsewhere and is definitely not bound to the $timeout, but also to the notimeout method (ignoring the error, of course, but that can be easily fixed by checking for $scope.$$phase first).
Also, when you click the original links, both of them, the closing delay is there.
So, in 4 cases you get the same delay, which means it's something in the code. I'll give it another look and update the answer if I find what's causing it.

Related

AngularJS Bootstrap Popover custom trigger

In my WebApp I want to show a customized popover that will be triggered when a variable in my controller is set. To achieve this I've created a custom trigger event and set it with $tooltipProvider.setTriggers({"showChat": "hideChat"}]
Here is a plnkr.co of my code that does not working.
I also checked other solutions here on StackOverflow (like AngularJS Bootstrap Tooltip - trigger on event or Good way to dynamically open / close a popover (or tooltip) using angular, based on expression?) that seem to work, but I can't figure out where my code is wrong.
I assume it's just a little thing (like always.. :-) )
Angular-bootstrap seems to use addEventListener to subscribe to this event, which means you cannot trigger it with .trigger(), what you'll need is dispatchEvent:
if(scope.showPopover) {
console.log('trigger showChat')
element.get(0).dispatchEvent(new Event("showChat"));
} else {
console.log('trigger hideChat')
element.get(0).dispatchEvent(new Event("hideChat"));
}
See in this fixed plunker.
Btw: unless you do something more complicated in your app, you can simply use popover-is-open="showPopover" to trigger the popover, like in this plunker

Angularjs focus unexpectedly requires an async setTimeout to work

Ok StackOverflow, riddle me this.
I have a button that I click on which toggles the visibility of an input. In the scope function which changes the visibility I use javascript to set focus on that input tag. What I want to know is why does it not work unless I wrap it in a setTimeout?
$scope.toggle = function() {
if ($scope.hideInput) {
setTimout(function(){
input.focus();
}, 0); // That's right, zero!
}
$scope.hideInput = !scope.hideInput;
};
Here's a working jsfiddle with a button that correctly sets the focus and a button that doesn't:
http://jsfiddle.net/PsS99/1/
Can tell you the reason why.
First you can't focus element, with css display:none.
So in the one (broken) method, you focus the element before it is actually displayed.
Because the angular will go line to line and $scope.hideInput = !scope.hideInput; will trigger the watcher for a digest-loop. In that loop your input will be set to display: block.
The timeout will move the focus command into it's own thread, and the $scope.hideInput = !scope.hideInput; will trigger the digest, too.
But the digest is curiously fast enought to display the element before the input.focus(); will be executed.
This is my explanation but understanding this, did help me in some curious problems of angular's timing.
Maybe use this ? http://ngmodules.org/modules/ng-focus-on
A directive to set the focus by variable.

Wrote alternative to jQuery Accordion, it spazzed. Why?

I wrote an alternative to the jQuery Accordion, as that didn't offer multiple open section support (any idea why they opted to not include support for that? What's the history there?). I did some research on StackOverflow, as well on Google to see what other options others came up. I needed something that could be used on the fly on multiple elements.
After seeing several solutions and experimenting with them, in the end, I wrote my own version (based on Kevin's solution from http://forum.jquery.com/topic/accordion-multiple-sections-open-at-once , but heavily modified).
jsFiddle can be found here: http://jsfiddle.net/3jacu/1/
Inline Code:
$(document).ready(function(){
$.fn.togglepanels = function(){
return this.each(function(){
h4handler = $(this).find("h4");
$(h4handler).prepend('<div class="accordionarrow">▼</div>');
$(h4handler).click(function() {
$(h4handler).toggle(
function() {
barclicked = $(this);
$(barclicked).find(".accordionarrow").html('►');
$(barclicked).next().slideUp('slow');
window.console && console.log('Closed.');
return false;
},
function() {
barclicked = $(this);
$(barclicked).find(".accordionarrow").html('▼');
$(barclicked).next().slideDown('slow');
window.console && console.log('Open.');
return false;
}
);
});
});
};
$("#grouplist").togglepanels(); }
Oddly, the accordion arrow at the right side stopped working once I pasted it in jsFiddle, while it works in my local copy.
In any case, the issue is that toggling isn't working as expected, and when it does, it fires duplicate toggle events which result in it closing, opening, then ultimately closing the section and it won't open from that point on (it toggles open then closes back). That's assuming it works! At first, it won't work as it doesn't respond. I think there's a logic error somewhere I'm missing.
From what I wrote/see in the code, it searches the given handle for the corresponding tag (in this case, h4), pops the handle into a variable. It then adds the arrow to the h4 tag while applying the accordionarrow class (which floats it to the right). It then adds a click event to it, which will toggle (using jQuery's toggle function) between two functions when h4 is clicked.
I suspect the problem here is that I may be mistakenly assuming jQuery's toggle function will work fine for toggling between two functions, that I'll have to implement my own toggle code. Correct me if I'm wrong though!
I'm trying to write the code so it'll be as efficient as possible, so feedback on that also would be appreciated.
Thanks in advance for your time, assistance, and consideration!
You have the toggle binding (which is deprecated by the way) inside of the click binding, so a new event handler is getting attached every time you click the header.
As a random aside you should also fire events within the plugin (where you have the console lines would make sense) so that external code can react to state changes.
I believe your issue is the $(h4handler).click(function() { you have wrapped around the toggle listener. Essentially what this was doing was making so every click of the tab was adding the toggle listener, which was then also firing an event. Removing the click listener will have the behaviour you expect.
You forgot to paste the trailing characters ); to close the function call to jQuery function ready. Fixed: http://jsfiddle.net/LeZuse/3jacu/2/
UPDATE: I've just realised I did not really answer your question.
You are duplicating the .toggle functionality with binding another .click handler.
The doc about .toggle says:
Description: Bind two or more handlers to the matched elements, to be executed on alternate clicks.
Which means the click event is already built in.
NOTE: You should use local variables instead of global, so your plugin won't pollute the window object. Use the var keyword for this:
var h4handler = $(this).find("h4");

Sharing an event across two scripts or using change is visibility state as an event trigger

OK I am lost here. I have read numerous postings here and else where on the topic of how to check for the state of a given element in particular whether it is visible or hidden and make the change of state trigger an event. But I cannot make any of the suggested solutions work.
Firstly, as every one seems to leap on this point first, the need to do this has arisen as I have one jQuery script which deals with displaying an svg icon in a clickable state. And another which already has functions to perform relevant actions when the form is made visible by clicking the icon and obviously I want to reuse these.
What I have tried:
Initially I tried have both the scripts acting on a single click event (this remains the ideal solution)....
Script 1:
$(".booked").on("click", function() {
$("#cancellation_Form_Wrapper").css("visibility","visible");
}).svg({loadURL: '../_public/_icons/booked.svg'});
Script 2:
$(".booked").on("click", function() {
// do stuff
});
This did not work so I tried to research sharing an event across two scripts but couldn't make any head way on this subject so I tried triggering another event for the second script to pick up....
Script 1:
$(".booked").on("click", function() {
$("#cancellation_Form_Wrapper").css("visibility","visible");
$("#cancellation_Form_Wrapper").trigger("change");
}).svg({loadURL: '../_public/_icons/booked.svg'});
Script 2
$("#cancellation_Form_Wrapper").on("change", function(event){
// do stuff
});
This did not work again I am unclear why this didn't have the desired effect.
Then I tried is(:visible) ....
Script 1
$(".booked").on("click", function() {
$("#cancellation_Form_Wrapper").css("visibility","visible");
}).svg({loadURL: '../_public/_icons/booked.svg'});
Script 2
$("#cancellation_Form_Wrapper").is(":visible", function(){
// do stuff
});
So I am a bit lost. The ideal would be to return to the first notion. I do not understand why the click event on the svg cannot be handled by both scripts. I assume that this has something to do with event handlers but I am not sure how I could modify the script so they both picked up the same event.
Failing that I could use the fact the visibility state changes to trigger the action.
Any guidance welcomed.
Edit Ok I have just resolved the issue with script 2 picking up the triggered event from script 1. Sad to say this was a basic error on my part ... the form was preventing the display of the alert. However I still cannot get the is(:visible) to work.
Your syntax may be off:
$("#cancellation_Form_Wrapper").is(":visible", function(){
// do stuff
});
should be:
if($("#cancellation_Form_Wrapper").is(":visible")){
// do stuff
});
EDIT: If you want something to happen after a div is made visible, you may want to use the show() callback rather than toggling visibility:
$('#cancellation_Form_Wrapper').show(timeInMilliseconds, function(){
// do something
});
However, this needs to take place in the same function, which I don't think improves your position.
The problem is probably that your second on() script is firing at the same time as your first, meaning the wrapper is not yet visible. You could try wrapping the second on() in a short timeout:
$(".booked").on("click", function() {
setTimeout(function(){
if($("#cancellation_Form_Wrapper").is(":visible")){
// do stuff
});
}, 100);
});
That should introduce enough of a delay to make sure the wrapper has been shown before trying to execute the second statement.

JQuery if statement for .is(':visible') not working

I have a div that when the page is loaded is set to display:none;. I can open it up using this simple code:
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").css({'display':'block'});
});
Once it's opened, I'd like the user to be able to close it so I tried using the .is(':visible') function and then wrapping my original code in an if statment but this time using display:none;
if($('.field-group-format-wrapper').is(':visible')){
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").css({'display':'none'});
});
}
This does not seem to work though and I am not getting any syntax errors that I know of.
I also tried this:
if ($('.field-group-format-wrapper').is(':visible'))
$(".field-group-format-toggler").click(function () {
$(".field-group-format-wrapper").css({'display':'none'});
});
... but that did not work either.
You can just use the toggle function:
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").toggle();
});
This will show the '.field-group-format-wrapper' elements if they are currently hidden and hide them if they're currently visible.
FYI the reason your code snippet in your question wasn't working is because you're only checking the visibility of the elements on dom ready, rather than on each click - so the event handler to show the elements will never be attached.
I guess your function is only being called on page load at which time all divs are hidden.
Why not check the visibility in the click event handler?
$('.field-group-format-toggler').click(function(){
var $wrapper = $('.field-group-format-wrapper'); //Maybe $(this).parent()?
if($wrapper.is(':visible'))
$wrapper.hide();
else
$wrapper.show();
As already mentioned, you can use the toggle function to achieve what you want.
To add a bit of extra information, when attaching events like you're doing, you're actually using a subscription model.
Registering an event puts it in a queue of events subscribed to that handler. In this case, when you add the second event to change the CSS, you're adding an event, not overwriting the first one.
Whilst thing isn't actually causing your problem, it's worth being aware of.

Categories

Resources