jQuery autocomplete styling on only certain results - ajax

Is it possible to add styling to only some of the results returned in an autocomplete dropdown?
The code below works fine, however, I would like to style the individual results based on the value of data[x].restricted. When it is true, I still want to display those items but disable or grey them out within the autocomplete dropdown list. If data[x].restricted is false then I do not want to apply any additional styling to those items.
source: function (request, response) {
$.ajax({
url: $("#AutoCompleteCustomerNameUrl").val(),
type: "POST",
dataType: "json",
data: {
srchCus: request.term
},
success: function (data) {
var x, array = [];
for (x in data) {
array.push({
label: (data[x].restricted ? 'Restricted Access - ' : '') + data[x].customerName,
name: data[x].customerFullName
});
}
}
});
}
Any assistance on how to accomplish this would be much appreciated.

Approach 1 (feels hacky)
This doesn't feel like a great option, but you could probably do this by using jQuery to target elements based on their text content. Autocomplete suggestions are generated as <li>s, so something like this might work:
$('li:contains("Restricted Access")').addClass('grey');
The question is then when to run that? Those elements are added dynamically of course, after page load, so that would have to run after they've been created - you'd have to run it based on some autocomplete event. Looking through the list in the docs, maybe the open event would be best. A handler for that event will run whenever the menu is opened, so it could add a CSS class to all the just-created suggestions matching that selector. Eg (untested):
$("#selector").autocomplete({
// ... your normal autocomplete code ...
open: function(event, ui) {
// Add a CSS class to those suggestions matching the text
$('li:contains("Restricted Access")').addClass('grey');
}
});
I haven't tested this, as it doesn't feel like the right approach. Below is a much better, tested and working option.
Approach 2 (feels good)
You can also do this using the _renderItem extension point. If you check the example they give there you can see it is the function which actually generates the HTML which shows up as your autocomplete suggestion. If we can customise that, we could do anything - eg check details of the item, add specific CSS classes, etc.
I don't find those docs super clear, but it isn't hard to find examples of it in use, eg the Custom Data example (that #Simon-K linked to in the comments above) shows how to use it:
$("#selector").autocomplete({
// ... your normal autocomplete code ...
}).autocomplete("instance")._renderItem = function(ul, item) {
// Here we have complete control of what is returned, and access to
// the items!
return $("<li>").append("<div>" + item.label ...).appendTo(ul);
};
So with your requirements, we could do something like this:
$("#selector").autocomplete({
// ... your normal autocomplete code ...
}).autocomplete("instance")._renderItem = function(ul, item) {
var style = (item.restricted) ? 'grey' : '';
return $("<li>")
.append("<div class='" + style + "'>" + item.label + "</div>")
.appendTo(ul);
};
And then of course add a CSS class to style those items:
.grey {
color: #ccc;
}
Working JSFiddle.

Related

SAPUI5: Extend Control, renderer has html tags with event

I extend a Control to create a new custom control in UI5 and this control renders a tree as UL items nicely. Now I need to implement a collapse/expand within that tree. Hence my renderer writes a tag like
<a class="json-toggle" onclick="_ontoggle"></a>
and within that _ontoggle function I will handle the collapse/expand logic.
No matter where I place the _ontoggle function in the control, I get the error "Uncaught ReferenceError: _ontoggle is not defined"
I am missing something obvious but I can't find what it is.
At the moment I have placed a function inside the
return Control.extend("mycontrol",
{_onToggle: function(event) {},
...
Please note that this event is not one the control should expose as new event. It is purely for the internals of how the control reacts to a click event.
I read things about bind and the such but nothing that made sense for this use case.
Took me a few days to crack that, hence would like to provide you with a few pointers.
There are obviously many ways to do that, but I wanted to make that as standard as possible.
The best suggestion I found was to use the ui5 Dialog control as sample. It consists of internal buttons and hence is similar to my requirement: Render something that does something on click.
https://github.com/SAP/openui5/blob/master/src/sap.ui.commons/src/sap/ui/commons/Dialog.js
In short, the solution is
1) The
<a class="json-toggle" href></a>
should not have an onclick. Neither in the tag nor by adding such via jQuery.
2) The control's javascript code should look like:
sap.ui.define(
[ 'sap/ui/core/Control' ],
function(Control) {
var control = Control.extend(
"com.controlname",
{
metadata : {
...
},
renderer : function(oRm, oControl) {
...
},
init : function() {
var libraryPath = jQuery.sap.getModulePath("mylib");
jQuery.sap.includeStyleSheet(libraryPath + "/MyControl.css");
},
onAfterRendering : function(arguments) {
if (sap.ui.core.Control.prototype.onAfterRendering) {
sap.ui.core.Control.prototype.onAfterRendering.apply(this, arguments);
}
},
});
control.prototype.onclick = function (oEvent) {
var target = oEvent.target;
return false;
};
return control;
});
Nothing in the init(), nothing in the onAfterRendering(), renderer() outputs the html. So far there is nothing special.
The only thing related with the onClick is the control.prototype.onclick. The variable "target" is the html tag that was clicked.

Assigning behavior to Fine-Uploader generated thumbs

I think I have almost the same problem as described here : fine-uploader generate more unique custom file ids
But I can't figure out a solution that fit my needs.
I have a modal window containing 2 tabs. Each tab contains a FineUploader instance. That works pretty well.
BUT
I want to assign a behavior to each file added (for exemple, assign an "onclick" behavior to a generated thumb).
To do this, I do something like this :
callbacks: {
onComplete: function(id, name, response) {
scope.thumbloaded({
id: id,
name: name,
uuid: response.uuid
});
},
And the scope.thumbloaded function do this :
function thumbloaded(id, name, uuid) {
$('#qq-file-id-' + id).on('click', function(e) {
e.stopPropagation(); // avoid the entire thumbnail to catch the event
doSomething();
});
}
The problem is that FU creates DOM elements with formatted ids, like qq-file-id-XXX.
So, when I add several files in one of each FU instance I have (in each of my tabs, remember?), FU creates two DOM elements with same ids. And the "click" event is added twice on elements with same ids.
Do you see the problem??
I wasn't able to find a solution yet.
Any help?
thanks.
Ok, I finally find out a workaround, based on jquery's selector.
Very easy though.
I just apply a certain class to each of element I just took care of. Though, I don't hook new event listener to elements that already have it.
The code will speak for itself:
function thumbloaded(id, name, uuid) {
var newThumb = $('.qq-file-id-' + id + ':not(.aw-thumbnail)');
newThumb.prop('id', uuid);
$('#' + uuid).on('click', function(e) {
doSomething();
});
$('#' + uuid).addClass('aw-thumbnail');
}

Jquery/Ajax: How to populate Multiple Fields

I am using the jquery/ajax autocomplete, below is the code snippet of my simple app:
external .jsp:
$(function () {
$("#inputfield").autocomplete({
source: '/fruitapp/findFruit'
});
});
and in my controller:
def findFruit = {
def fruitsearch= Fruit.withCriteria {
ilike 'fruit', params.term + '%'
}
render (fruitsearch?.'fruit' as JSON)
}
As you can see, it will only fill in a single text field which is the "inputfield". Now, I want that if I select an item from the autocomplete list, it will fill in at least two fields. How would I do it?
Thanks in advance.
The autocomplete can get a "select" event. There you can do whatever you want.
From their documentation
$( ".selector" ).autocomplete({
select: function(event, ui) { ... }
});
Bind to the select event by type: autocompleteselect.
$( ".selector" ).bind( "autocompleteselect", function(event, ui) {
...
});
Here is a fiddle I made from their example.
As you can see, once an autocomplete option is selected, I am updating 2 different divs.
This is the code responsible for it:
$( "#tags" ).autocomplete({
source: availableTags,
select:function(e,u){
console.log([e,u]);
$("#output").text("This is the label:" + u.item.label);
$("#more_output").text("this is the value:" + u.item.value);
}
});
And just to be perfectly clear - "u" can contains anything you want.
Here I modified the fiddle to contain a single complex JS object. You can access all the fields.
I added a log print for you to see. Use chrome's developer tools to see the log print and view objects' content.

Loading dynamic "chosen" select elements

I am using the jQuery plugin chosen (by Harvest). It is working fine on (document).ready, but I have a button that, when clicked, uses ajax to dynamically create more select objects that I want to use the "chosen" feature. However, only the original select elements have the "chosen" features, and the new (dynamically created) do not work. I am using jQuery.get to append the new elements. Here is a sample of the code:
jQuery(".select").chosen();//this one loads correctly
jQuery("#add-stage").click(function() {
jQuery.get('/myurl',{},function(response) {
//response contains html with 2 more select elements with 'select' class
jQuery('#stages').append(response);
jQuery(".select").chosen();//this one doesn't seem to do anything :-(
});
});
I was thinking that I need a .live() function somewhere, but I haven't been able to figure that out yet. Any help is much appreciated!
Note - I am not trying to dynamically load new options, as specified in the documentation using trigger("liszt:updated");
Ensure that the response elements have the select class.
console.log( response ); // to verify
May also be a good idea to only apply the plugin to the new element(s).
jQuery(".select").chosen();
jQuery("#add-stage").click(function() {
jQuery.get('/myurl',{},function(response) {
console.log( response ); // verify the response
var $response = $(response); // create the elements
$response.filter('.select').chosen(); // apply to top level elems
$response.find('.select').chosen(); // apply to nested elems
$response.appendTo('#stages');
});
});
Also, if /myurl is returning an entire HTML document, you may get unpredictable results.
after you code (fill the select) .write this
$(".select").trigger("chosen:updated");
I had a similar problem with Chosen. I was trying to dynamically add a new select after the user clicks on a link. I cloned the previous select and then added the clone, but Chosen options would not work. The solution was to strip the Chosen class and added elements, put the clone in the DOM and then run chosen again:
clonedSelect.find('select').removeClass('chzndone').css({'display':'block'}).removeAttr('id').next('div').remove();
mySelect.after(clonedSelect);
clonedSelect.find('select').chosen();
one way you can use chosen with ajax:
$.ajax({
url: url,
type: 'GET',
dataType: 'json',
cache: false,
data: search
}).done(function(data){
$.each(data, function(){
$('<option />', {value: this.value, text: this.text}).appendTo(selectObj);
});
chosenObj.trigger('liszt:updated');
});
where selectObj is particular select object
But ...
Chosen is implemented very bad.
It has several visual bugs, like: select some option, then start searching new one, then remove selected and the keep typing - you will get 'Select some options' extended like 'Select some options search value'.
It doesn't support JQuery chaining.
If you will try to implement AJAX you will notice, that when you loose focus of chosen, entered text disappears, now when you will click again it will show some values.
You could try to remove those values, but it will be a hard time, because you cannot use 'blur' event, because it fires as well when selecting some values.
I suggest not using chosen at all, especially with AJAX.
1.- Download Livequery plugin and call it from your page.
2.- Release the Kraken: $(".select").livequery(function() { $(this).chosen({}); });
This is an example of Chosen dynamically loading new options form database using ajax every time Chosen is clicked.
$('.my_chonsen_active').chosen({
search_contains:true
});
$('.my_chonsen_active').on('chosen:showing_dropdown', function(evt, params){
id_tosend=$(this).attr("id").toString();
$.get("ajax/correspondance/file.php",function(data){
$('#'+id_tosend).empty();
$('#'+id_tosend).append(data);
$('#'+id_tosend).trigger("chosen:updated");
});
});

Using jQGrid and jQUery tabs how to oprimize the number of grids

I have 3 different tabs where i am displaying data using jQGrids(each tab contain one grid).
But i just thought that my grids are completely the same, only the difference is that they using different url to get data.
So I have three similar girds on each tab only with different urls:
First: url: '/Home/GetData?id=1' Second: url: '/Home/GetData?id=2' and Third: url: '/Home/GetData?id=3'
So i was thinking that may be i may declare grid only once and than on each tab click a can pass the url to load data? So on each tab click jQGrid will be populating from the new url.
May be some one may have any ideas about that?
Or may be some one may have better ideas how to reduce "jQGrid copy-paste" in that case?
UPDATE 0:
Nearly get it work i mean it is working but there is one small problem,
When i am switching tabs the header of the grid getting lost...and some jqgrid formatting as well.
here is my code:
$("#tabs").tabs({
show: function (event, ui) {
if (ui.index == 0) {
//$("#Grid1").appendTo("#tab1");
//$("#Grid1Pager").appendTo("#tab1");
//When Appending only pager and grid div, header getting lost so i've append the whole grid html instead
$("#gbox_Grid1").appendTo("#tab1");
changeGrid("#Grid1", 1);
}
else if (ui.index == 1) {
//$("#Grid1").appendTo("#tab2");
//$("#Grid1Pager").appendTo("#tab2");
$("#gbox_Grid1").appendTo("#tab2");
changeGrid("#Grid1", 2);
}
else if (ui.index == 2) {
//$("#Grid1").appendTo("#tab3");
//$("#Grid1Pager").appendTo("#tab3");
$("#gbox_Grid1").appendTo("#tab3");
changeGrid("#Grid1", 3);
}
}
});
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$(grid).trigger('reloadGrid');
}
UPDATE 1
All right, i've changed the code to append the whole grid instead of appending grid div and pager only. So it is working like that.
You can basically make the tabs as regular buttons that will call some function which sets new URL parameter to the grid and reloads it.
The function should be something like this:
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id, page: 1
});
$(grid).trigger('reloadGrid');
}
Note that I set the page to 1. Keep in mind that you might need to set the default sorting column or something similar depending on your solution.
UPDATE
If you really want to go with the tabs, the 'show' event handler can be simplified.
Try this:
$("#tabs").tabs({
show: function (event, ui) {
var id = ui.index + 1;
$("#gbox_Grid1").appendTo("#tab" + id);
$("#Grid1").jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$("#Grid1").trigger('reloadGrid');
}
});

Resources