I am trying to use JQuery UI Dialog from AngularJS partial HTML pages.
The problem is that the dialog works fine, and even can be opened multiple types, from a view - as long as the view is opened for the 1st time.
Yet when navigating to another view, and then back to the view using the dialog - an attempt to open the dialog works as if there would be 2 dialogs to show, not one. Browser "refresh" of the view URL makes the problem disappear, till the next back-and-forth navigation.
I put a test application (not integrated with real back end) to show the problem:
Navigate to:
http://socialtodow.cloudapp.net/app/tdg_test/#!/new
Click on red "Get Advice" button: the dialog shows up OK, and can be closed with Esc.
Now, please navigate to http://socialtodow.cloudapp.net/app/tdg_test/#!/search and then back to http://socialtodow.cloudapp.net/app/tdg_test/#!/new
Click red "Get Advice". You get 2 dialogs, one on top of another - the buggy behavior I am talking about.
The relevant directive code:
.directive('advice', [function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var self = $(element),
adviceDialog = $(attrs.advice);
adviceDialog.dialog({
width: 260,
height: 405,
autoOpen: false,
modal: true,
dialogClass: "advice_dialog"
});
self.click(function () {
adviceDialog.dialog('open');
});
adviceDialog.find('button').click(function() {
adviceDialog.dialog('close');
});
}
}
}])
Checking
adviceDialog.is('dialog'))
does not seem to work - it's always true.
I am looking for a fix (not infrastructure replacement).
Any hint will be appreciated.
Max.
This is the modification to the directive, found by a developer wokring on the project, that seems to resolve the issue:
.directive('advice', [function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var self = $(element),
adviceDialog = $(attrs.advice);
self.click(function () {
adviceDialog.dialog({
width: 260,
height: 405,
autoOpen: false,
modal: true,
dialogClass: "advice_dialog",
close: function() {
adviceDialog.dialog('destroy');
}
});
adviceDialog.dialog('open');
});
adviceDialog.find('button').click(function() {
adviceDialog.dialog('close');
});
}
}
}])
Note: the URl mentioned in the original question may not be operational - sorry for that.
Max.
Related
How can I make tooltip do not be closed if input has focus? It works when it gets focus with tab, but if I use mouse to focus on input, tooltip will be closed on mouseout even if input has focus.
I can do
$('input').tooltip().off("mouseover mouseout");
But this will dissable tooltip on hover and I just need to dissable mouseout when input has focus.
http://jsfiddle.net/3dX6d/2/
http://jsfiddle.net/3dX6d/3/
Try this:
Working Example
$("input").tooltip({
close: function (event, ui) { //when the tooltip is supposed to close
if ($('input').is(':focus')) { // check to see if input is focused
$('input').tooltip('open'); // if so stay open
}
}
});
New and Improved Method:
Better Working Example
$("input").tooltip({
hide: {
effect: 'explode'// added for visibility
}
}).mouseleave(function () { // on mouse leave
if ($('input').is(':focus')) { // if input is focused
ui.tooltip.preventDefault(); //prevent the tooltip's default behavior
$('input').tooltip('open'); // leave the tooltip open
}
}).focusout(function () { // on focus out
$('input').tooltip('close'); // close the tooltip
});
API documentation:
:focus
event.preventDefault()
.focusout()
open method
close event
Instead of adding all these other listeners, I looked into the actual and decided the most effective way is to just inherit the widget and add an extra flag
http://code.jquery.com/ui/1.10.3/jquery-ui.js
Here's a demo
http://jsfiddle.net/3dX6d/7/
(function ($) {
$.widget("custom.tooltipX", $.ui.tooltip, {
options: {
focusPreventClose: false
},
close: function (event, target, content) {
if (this.options.focusPreventClose && $(this.element).is(":focus")) {
// Don't call the parent's close() call but therefore have to add event on focus out
this._on({ focusout:this.close });
} else {
this._superApply(arguments);
}
}
});
}(jQuery));
$('input').tooltipX({
focusPreventClose: true
});
Compared to the other solution, this doesn't require us to do more work with the extra open calls (which actually does several other calls within it). We simply prevent the tooltip to close when we have the focus on the element, as requested by original post.
The only thing you need to do to fix the ui error is just pass it in as a parameter per documentation.
http://api.jqueryui.com/tooltip/#event-close
$(document).tooltip({ selector: "[title]" }).mouseleave(function(event, ui) {
if ($('input').is(':focus')) {
ui.tooltip.preventDefault();
$('input').tooltip('open');
}
});
I have a page on which I am loading a select tag with options that I am getting from an ajax call to a service. It is working just fine and loading the select tag correctly. I only want this loading to happen once when the user first arrives at the page so I put the call to the function that does this inside the $(document).ready(function call. The problem that I am seeing is that when the user selects one of the options and then clicks a button under certain circumstances I pop a dialog (jQuery UI) telling them they need to take a certain action. The first time this happen and only the first time the selected option is getting reset somehow to the first option in the list. I ran in debug many times and discovered that if this is the first time on the page the selector is loaded on arrival at the page as is expected but if the dialog gets popped it is loaded again - only the first time - after that if the dialog gets popped the reload does not occur and all is well. The abbreviated code:
$(document).ready(function () {
$.fn.LoadStuff();
});
jQuery.fn.LoadStuff = function () {
//load up the select tag with options
};
LoadStuff does not get called anywhere else. selOffers is the select tag and dvConflict is the dialog. They are not sharing a common parent div.
<select id="selOffers"></select>
<div id="dvConflict"><div id="dvConflictMsg" /></div>
jQuery for the dialog:
var optSave = {
width: 400,
modal: true,
resizable: false,
draggable: false,
closeOnEscape: true,
zIndex: 1320,
buttons: [
{
text: 'Ok',
click: function () {
$(this).dialog('close');
}
}
]
}
$(".ui-dialog-titlebar").hide(); //Hides the title
$('#dvConflict').css('background', 'none'); //Takes out our custom background
$('#dvConflict').dialog(optSave);
EDIT: Two things
Use $.LoadStuff = function () { } instead of $.fn.LoadStuff = function () {...}. The first is to be called at any context via $.LoadStuff();. The latter is to be used on an element-selection, like $("div.to-load").LoadStuff();.
$(document).ready is fired every time the DOM finishes loading. In some AJAX calls, you could be reloading parts of your DOM, or an internal frame (I don't know, though I don't have your code).
The following code sample will help you bypass the problem:
var first = true;
$(document).ready(function () {
if (first) $.LoadStufF();
...
first = false;
}
When opening a dialog, make sure there are no <script> tags inside the dialog-wrapped element. Say you have the code line:
$('#dialoged').dialog({ ... });
So a bad practice is to have:
<div id="dialoged">
<script>
var first = true;
$(document).ready(function () {
if (first) $.LoadStufF();
...
first = false;
}
</script>
</div>
Currently I am working on a project for which I use the jQuery UI Accordion.
Therefore I initialise the accordion on an element by doing
<div id="accordion"></div>
$('#accordion').accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
After init the accordion I append some data coming from an AJAX request. (depends on user interaction)
In a simplified jsfiddle - which does exact the same thing as the ajax call - you can see how this looks like.
So far it seems to be working quite well but there is one problem I face.
In my initialisation I say that I want all panels to be closed but after calling refresh on the accordion everything of those settings seems to be gone and one panel opens.
Note that I implemented jQuery UI v1.10.2 in my fiddle. Update notes say
The refresh method will now recognize panels that have been added or removed. This brings accordion in line with tabs and other widgets that parse the markup to find changes.
Well it does but why has it to "overwrite" the settings I defined for this accordion?
I also thought about the possibility that it might be wrong to create the accordion on an empty <div> so I tested it with a given entry and added some elements afterwards.
But the jsfiddle shows exactly the same results.
In a recent SO thread I found someone who basically does the same thing as I do but in his jsfiddle he faces the same "issue".
He adds a new panel and the first panel opens after the refresh.
My current solution for this issue is to destroy the accordion and recreate it each time there's new content for it.
But this seems quite rough to me and I thought the refresh method solves the need to destroy the accordion each time new content gets applied.
See the last jsfiddle
$(document).ready(function () {
//variable to show "new" content gets appended correctly
var foo = 1;
$('#clickMe').on('click', function () {
var data = '';
for (var i = 0; i < 3; i++) {
data += '<h3>title' + foo + '</h3><div>content</div>';
foo++;
}
if ($('#accordion').hasClass('ui-accordion')) {
$('#accordion').accordion('destroy');
}
$('#accordion').empty().append(data).accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
});
});
Unfortunately it is not an option for me to change the content of the given 3 entries because the amount of panels varies.
So my questions are the one in the title and if this behaviour is wanted like that or if anybody faces the same problem?
For the explanation of this behaviour, have a look in the refresh() method of the jquery-ui accordion widget, the problem you are facing is at line 10 :
refresh: function() {
var options = this.options;
this._processPanels();
// was collapsed or no panel
if ((options.active === false && options.collapsible === true) || !this.headers.length) {
options.active = false;
this.active = $();
// active false only when collapsible is true
} if (options.active === false) {
this._activate(0); // <-- YOUR PROBLEM IS HERE
// was active, but active panel is gone
} else if (this.active.length && !$.contains(this.element[0], this.active[0])) {
// all remaining panel are disabled
if (this.headers.length === this.headers.find(".ui-state-disabled").length) {
options.active = false;
this.active = $();
// activate previous panel
} else {
this._activate(Math.max(0, options.active - 1));
}
// was active, active panel still exists
} else {
// make sure active index is correct
options.active = this.headers.index(this.active);
}
this._destroyIcons();
this._refresh();
}
Here we go, the variable $thislistitem, in bold below, is in the same place in my function. I seperated it to bold it. Focus on the jquery ui buttons being created and the click events.
function activatequalifdetails($subgrid, qualId){
$subgrid.find('.itemcontrols').hide();
$subgrid.find("#detailedsubjects ol").on("click", "li", function(event){
event.stopPropagation();
var $itemwithfoucsclass = $(".focus");
/* <![CDATA[ */ if(($itemwithfoucsclass[0] != $(this)[0]) && ($itemwithfoucsclass.length !== 0)){ /* ]]> */
$.post('<c:url value="/highschooldetailedqualifications/highschoolqualdetailedajaxupdate/"/>'+$itemwithfoucsclass.find('.itemcontrols button:nth-child(1)').attr("qualdetailid"), {grade: $itemwithfoucsclass.find('.grade').val(), yearattained: $itemwithfoucsclass.find('.yearAttained').val()});
}
var $thislistitem = $(this);
$subgrid.find('#detailedsubjects ol li').not(this).removeClass('focus').find('.itemcontrols').hide();
$(this).addClass("focus").find('.itemcontrols').show();
$(this).find('.itemcontrols button:nth-child(1)').button({
icons: {
primary: "ui-icon-disk"
},
text: false
}).unbind('click').click(function(){
$.post('<c:url value="/highschooldetailedqualifications/highschoolqualdetailedajaxupdate/"/>'+$(this).attr("qualdetailid"), {grade: $thislistitem.find('.grade').val(), yearattained: $thislistitem.find('.yearAttained').val()}, function(data){
$thislistitem.removeClass('focus').find('.itemcontrols').hide();
});
});
$(this).find('.itemcontrols button:nth-child(2)').button({
icons: {
primary: "ui-icon-trash"
},
text: false
}).unbind('click').click(function(){
$.get('<c:url value="/highschooldetailedqualifications/highschoolqualdetailedajaxdelete/"/>'+qualId+'/'+$(this).attr("qualdetailid"), function(data){
$thislistitem.remove();
});
});
$(this).find('.itemcontrols button:nth-child(3)').button({
icons: {
primary: "ui-icon-closethick"
},
text: false
}).unbind('click').click(function(){
$thislistitem.removeClass('focus').find('.itemcontrols').hide()
});
});//apply css class on click on any given item
}
The click events for the save and delete work perfectly. The click event for the cancel button however (3rd button) is giving some starnge behavior. The click logic for the 3rd button is identical to that of the save, only difference is the icon and no post id required.
When I click the cancel button, nothing happens.
If I do
.unbind('click').click(function(){
$thislistitem.remove();
});
for the 3rd button, it works.
If I alert some test text, it works fine. If use removeClass() nothing occurs.
I tried copying and pasting the save function as is, change the icon and so on and left the ajax call intact. this worked fine and the focus class was removed. If I remove the ajax call and run the logic as seen in the function above, well nothing happens. Firebug reports nothing.
I have several versions of jquery loaded on this page, (1.4.3, 1.4.2, 1.5.1, 1.6.2 & 1.7.2), in that order. Could this be my problem ? I need the different versions for different plugins i am using for other things. Any ideas ?
I am totally stumped.
Ok so this is what I had to do, looks like a very very fundimental mistake on my behalf.
$(this).find('.itemcontrols button:nth-child(3)').button({
icons: {
primary: "ui-icon-closethick"
},
text: false
}).unbind('click').click(function(){
$thislistitem.removeClass('focus').find('.itemcontrols').hide();
return false;
});
All i needed to do was add return false; after my logic. Hope it helps someone out there.
Edited to add the solution suggested by #alistair-laing.)
After reading this post reply by #jek, I could make multiple links on my page that would pass an id variable through the URL so that the content of the dialog could be loaded in on the fly. However, I really want this to be a modal dialog:
(edited to include the fix; nb: the original script was in the post linked to above, all I did was break it)
$(function (){
$('a.ajax').click(function() {
var url = this.href;
var dialog = $('<div style="display:none"></div>')
.appendTo('body')
// load remote content
dialog.load(
url,
{},
function (responseText, textStatus, XMLHttpRequest) {
dialog.dialog({
modal: true,
width: 500
});
}
);
//prevent the browser to follow the link
return false;
});
});
quiet a few things. Try move the content from your first .dialog into your second .dialog which you call as part of the the .load callback. What youare doing is creating dialog then injecting content into it only to call it again. You could also remove the the autoOpen so that the dialog opens with the content.