Redisplay results list on AutoCompleteExtender through javascript - javascript

I have a working AutoCompleteExtender implementation.
What I want, is that if I have exited the text box, and the list of items have dissappeared, I want to re-display the list from javascript code without having to write something in the text box again (just redisplay list based on current filter value in text box by click on a button or something). I know how to get the AutoCompleteExtender Behaviour object from code, so all I need is to know the javascript API on that object that enables me to redisplay the list.
I have tried this as suggested in the comments on this answer, but not working:
AutoCompleteEx.showPopup();
I have also tried this as suggested in this answer, but not working:
AutoCompleteEx._onTimerTick(AutoCompleteEx._timer, Sys.EventArgs.Empty);
EDIT:
After some investigation in the back end code used by the AutoComplete, I think maybe the problem is that once shown, it checks on future calls if the value in the search box has changed since last time, and if not it doesn't show it again. I have not found out how to come around this. I have tried different approaches to reset the value, and then set the value again, but with no success.

Enjoy :). That's was an interesting task.
function redisplayAutocompleteExtender() {
var extender = $find("AutoCompleteEx");
var ev = { keyCode: 65, preventDefault: function () { }, stopPropagation: function () { } };
extender._currentPrefix = "";
extender._onKeyDown.call(extender, ev);
}
Or you can set EnableCaching property to true on extender and use script below. This solution allows to avoid additional web service call.
function redisplayAutoComplete() {
var extender = $find("AutoCompleteEx");
var textBox = extender.get_element();
textBox.focus();
var showSuggestions = function(){
extender._update.call(extender, textBox.value, extender._cache[textBox.value], true);
};
setTimeout(showSuggestions, 0);
}

Related

Looping through multiple checkboxes and adding the checked values to an array to write back to Sharepoint

I have a block of code that is supposed to look at which checkboxes are checked and then write the value of those checkboxes to an array so that I can write the data back to Sharepoint. I got it working before when I used an if/else statement, but I need it to be able to handle multiple checkboxes being selected. I did some research and found part of my code from another answer and combined it with some of my code but it doesn't seem to be working and I'm not sure why. None of the other answers I came across seem to be what I'm looking for.
Basically, what I'm asking is, what is the best way to achieve what I'm trying to do?
function addCustomers() {
$(document).ready(function () {
var customers = [];
var ckbox = $('.customer-options');
$('input').on('click', function () {
if (ckbox.is(':checked')) {
customers.push(ckbox).val();
}
else {
}
});
customers.toString();
document.getElementById('mxID04').innerHTML = customers;
});
}
It's not exactly clear what you are trying to do. First, your function contains document.ready which doesn't look right. Then you create some vars which I think you want to fill because at the end you are setting another element's innerHTML to, but in between you've just created a click handler which isn't going to work. Here's what I've put together for testing. I think you should be able to modify my example into what it is you're after
function addCustomers() {
var customers = [];
var ckbox = $('.customer-options');
alert('add handler');
$('input').change(function() {
$('input:checked').each(function() {
customers.push(this);
});
});
}
$( document ).ready(function() {
// alert('here');
addCustomers();
});

How can I disable automatic filtering in selectize.js? Built-in / plugin / modilfy source?

I have a selectize.js drop-down, which loads a list of items from the server using ajax. The server provides an autocomplete from the given string, so I don't need selectize's native filtering. Besides, I really need to turn it off: The server output may be totally different from selectize's one.
The data is fed into JavaScript objects fine, but selectize doesn't even show a popup, since those items doesn't match selectize's filter. How can I disable or modify native filtering and the matches highlighting algorithm? Either with a built-in option, or with a plugin? Or is the only way to go to modify the source?
EDIT:
searchField: false / function() doesn't work (and documentation doesn't mention them as available option values)
EDIT2:
Eventually came up with this trick: Add a fake field to each item, assign a search string to it and tell selectize to use is as a searchField. But obviously, there should a better way, so the question is still open.
I use this solution (if results from server is ordered correctly):
score: function() { return function() { return 1; }; },
or this (if need order)
score: function(search) {
var score = this.getScoreFunction(search);
return function(item) {
return 1 + score(item);
};
},
Sifter uses the score function to filter. Result of score must be > 0.
I solved with onInitialize method in selectize parameters:
$("select").selectize({
onInitialize: function() {
this.$control_input.attr('readonly', true);
}
});
I needed to disable searching so iPhones won't be displaying the keyboard. The solution I settled on makes the search field readonly by hooking into the selectize setup (without modifying the actual source, so selectize is still updatable). Here's the code, if anybody needs it:
// Put this code after you've included Selectize
// but before any selectize fields are initialized
var prevSetup = Selectize.prototype.setup;
Selectize.prototype.setup = function () {
prevSetup.call(this);
// This property is set in native setup
// Unless the source code changes, it should
// work with any version
this.$control_input.prop('readonly', true);
};
So, searching the code, I've found out, that Sifter module (searching/sorting engine, which Selectize relies on), it does have an option to disable filtering, we just need to forward it up, to Selectize. I can suggest the following patch:
Locate the function getSearchOptions() in Selectize main .js file:
https://github.com/brianreavis/selectize.js/blob/master/dist/js/selectize.js
Here is the before:
getSearchOptions: function () {
var settings = this.settings;
var sort = settings.sortField;
if (typeof sort === 'string') {
sort = [{field: sort}];
}
return {
fields: settings.searchField,
conjunction: settings.searchConjunction,
sort: sort
};
}
And here's the after: (added a comma, 5 lines of comments, and the patch itself)
...
getSearchOptions: function () {
var settings = this.settings;
var sort = settings.sortField;
if (typeof sort === 'string') {
sort = [{field: sort}];
}
return {
fields: settings.searchField,
conjunction: settings.searchConjunction,
sort: sort,
// A patch to allow to disable native filtering, in the case,
// when we want to provide search results on the server side.
// Negative form of the setting is to avoid changing the standard
// behaviour, (and, possibly, ruining the existing code), when this
// parameter is missing.
filter : !settings.dontFilter
};
},
...
Sorry, I just don't have time to create a branch on Github, the project deadline is near, and also, actually not sure, that I'll manage to be a fine contributor for now, due to some lack of experience working in Github. So, just posting a quick workaround.
With a little bit of CSS and a little bit of JS we can create this. And it looks perfect.
var select = $("#my-select-input");
$(select).next().find("div.selectize-input").addClass("no-searchable"); // Adding style to the div
$(select).next().find("div.selectize-input > input").addClass("no-searchable"); // Adding style to the input
$(select).next().find("div.selectize-input > input").prop("readonly", true); // Setting the input to read-only
$(select).next().find("div.selectize-input > input").prop("inputmode", "none"); // Guarantee in case it opens on the cell phone and click on the input no keyboard is opened
$(select).next().find("div.selectize-input > input").focus(function () { // Hack for when the search input gets the focus it will automatically blur.
$(this).blur();
});
.no-searchable {
cursor: pointer !important;
background-color: #FFFFFF !important;
}
.has-items input.no-searchable {
width: 1px !important;
}

Return parent function from child function on click

How can I return a parent function from a child function on click? Here's an example code:
function returnFunction() {
var output = false;
$('a').click(function() {
output = true;
});
return output;
}
var result = returnFunction();
The result will always be false since the click hasn't happened at the time the code is being run. How could I make this work though?
My intention is to call pop-up dialogs, and I'd like to have all the logic inside one function that can easily be loaded - including the click events of a Confirm dialog box.
Elsewhere in scripts I'd be calling it this way for example:
// Menu click triggers the dialog
$('a').click(function(e) {
// The function displays the dialog and returns the click events of the dialog
var result = returnFunction();
// If result was false, we'll prevent the menu access, for example
if (!result) {
e.preventDefault();
}
});
I'm aware of the jQuery UI dialog plugin. But I'd like to achieve this without it for now.
Thanks.
An over-simplification of it is:
Everything stops (including scrolling and clicking on hyperlinks) while executing javascript. This means you cannot "pause" the script until someone clicks on a link.
The typical way of solving this is a callback function:
function my_callback(some, arguments) {
// you can do whatever in here: an ajax load, set a global variable, change some of the page, ...
console.log(some, arguments);
alert(some + " " + arguments);
}
function returnFunction(callback, param) {
var output = false;
$('a').click(function() {
callback(param, "world");
});
}
returnFunction(my_callback, "hello");
demo at http://jsfiddle.net/UnBj5/
EDIT:
I did mention global variables because they are an option, but they are typically bad style. Try to use other means if possible.
If you want more help with it, provide more details of what you are trying to do.
Try using parameters instead. Send a parameter to a function that shows your alert boxes, show a different pop-up alert depending on the parameter, what you are trying to do won't work because its basically a chicken-egg problem.

Creating javascript objects using jQuery's each() method

Im curious about what might be a larger question than I think.
I am using the following code to listen for 'keyup' on a group of text input fields. If the user stops typing for a given amount of time, I send the data to a controller using AJAX.
I decided to try my hand at OOP in javascript to accomplish this. This is because I want a new instance of the timer method for each input field. (To be absolutely clear, Im very new to OOP in javascript so this might be dreadful. Let me know.)
Here is the main class with its methods:
function FieldListener(entity){
t = this;
t.typingTimer; // Timer identifier
t.doneTypingInterval = 1000; // Time in ms. e.g.; 5000 = 5secs
t.entity = entity;
entity.bind("keyup", function(){t.setTimer();});
}
FieldListener.prototype.setTimer = function(){
t = this;
// User is still typing, so clear the timer.
clearTimeout(t.typingTimer);
// Get the field name, e.g.; 'username'
t.entityType = t.entity.attr("name");
// If the value is empty, set it to a single space.
if(!(t.val = t.entity.val())){
t.val = ' ';
}
t.noticeSpan = t.entity.siblings("span");
// Display 'waiting' notice to user.
t.noticeSpan.html('...')
t.typingTimer = setTimeout(function(){t.doneTyping();},t.doneTypingInterval);
}
FieldListener.prototype.doneTyping = function(){
// Encode for passing to ajax route.
t = this;
valueType = encodeURIComponent(t.entityType);
value = encodeURIComponent(t.val);
$.ajax({
url: '/check/'+valueType+'/'+value,
type: 'GET',
processData: false
})
.done(function(validationMessage){
t.noticeSpan.html(validationMessage);
})
.fail(function(){
t.noticeSpan.html("Something went wrong. Please try again.");
});
}
So from here I'd like to be able to create an object of the FieldListener class for every input field.
I know I can do it easily if I have an id for each like so:
var fieldListener = new FieldListener($("#someFieldID"));
But I'd like to iterate over every field with a given class name. Something close to this perhaps?:
i = 0;
$(".info-field").each(function(){
i = new FieldListener($(this));
});
But that doesn't work (and doesn't look very nice).
Any thoughts? (Im also curious about critiques/improvements to the class/methods code as well.)
edit: As per #ChrisHerring's question: The issue is that it seems to create the object but only for the last element in the each() method. So the span associated with the last input field with the class '.info-field' displays the validationMessage returned from AJAX regardless of which field I am typing in.
UPDATE:
It seems like something is wrong with the creation of new objects. For example, if, rather than iterating through the each() method, I simply follow one class initiation with another, like so:
var fieldListener1 = new FieldListener($("#someFieldID"));
var fieldListener2 = new FieldListener($("#someOtherFieldID"));
that fieldListener2 overwrites variables being saved when initiating fieldListener1. This means that when I type into the input field with id "#someFieldID", it behaves as if I am typing into the input field with id "#someOtherFieldID". Thoughts?
UPDATE #2 (solved for now):
It seems that I have solved the issue for now. I needed to add 'var' before 't = this;' in the FieldListener class. Any comments/critiques are still welcome of course. ;)
The t variable is global. The function for the "keyup" event is evaluated dynamically which means it picks up the last value of t.
Change
t = this;
to
var t = this;
I think you want an array of FieldListener objects.
var myListeners = [];
i = 0;
$(".info-field").each(function(){
myListeners[i] = new FieldListener($(this));
i++
});
This'll give you a list of FieldListeners, where myListeners[0] is the listener for the first .info-field on the page, myListeners[1] is the listener for the second, etc.
Edit: It would appear you have solved the problem. This answer may still come in handy later on, though, so I won't delete it. =)
I think you should be using jquery's .on() to handle the binding.
$(body).on({
keyup: function () { HandleKeyUpEvent($(this)); },
keydown: function () { HandleKeyDownEvent($(this)); }
}, ".info-field");
I realize this is a departure from your original coding idea (using prototypes) but it will still be OOP, if that's what you intented to do.

Binding multiple events of the same type?

Firstly, is it possible? Been struggling with this one for hours; I think the reason my events aren't firing is because one event is unbinding/overwriting the other. I want to bind two change events to the same element. How can I do that?
As per request, here's the function I'm struggling with:
(function($) {
$.fn.cascade = function(name, trigger, url) {
var cache = {};
var queue = {};
this.each(function() {
var $input = $(this);
var $trigger = $input.closest('tr').prev('tr').find(trigger);
//$input.hide();
var addOptions = function($select, options) {
$select.append('<option value="">- Select -</option>');
for(var i in options) {
$select.append('<option value="{0}">{1}</option>'.format(options[i][0], options[i][1]));
}
$select.val($input.val()).trigger('change');
}
var $select = $('<select>')
// copy classes
.attr('class', $input.attr('class'))
// update hidden input
.bind('change', function() {
$input.val($(this).val());
})
// save data for chaining
.data('name', name)
.data('trigger', $trigger);
$input.after($select);
$trigger.bind('change', function() {
var value = $(this).val();
$select.empty();
if(value == '' || value == null) {
$select.trigger('change');
return;
}
// TODO: cache should be a jagged multi-dimensional array for nested triggers
if(value in cache) {
addOptions($select, cache[value]);
} else if(value in queue) {
$select.addClass('loading');
queue[value].push($select);
} else {
var getDict = {}
getDict[name] = value;
// TODO: use recursion to chain up more than one level of triggers
if($(this).data('trigger')) {
getDict[$(this).data('name')] = $(this).data('trigger').val();
}
$select.addClass('loading');
queue[value] = [$select];
$.getJSON(url, getDict, function(options) {
cache[value] = options;
while(queue[value].length > 0) {
var $select = queue[value].pop();
$select.removeClass('loading');
addOptions($select, options);
}
});
}
}).trigger('change');
});
return this;
}
})(jQuery);
The relevant chunk of HTML is even longer... but essentially it's a select box with a bunch of years, and then an <input> that gets (visibly) replaced with a <select> showing the vehicle makes for that year, and then another <input> that gets replaced with the models for that make/year.
Actually, it seems to be running pretty well now except for on page load. The initial values are getting wiped.
Solved the issue by pulling out that $select.bind() bit and making it live:
$('select.province').live('change', function() {
$(this).siblings('input.province').val($(this).val());
});
$('select.make').live('change', function() {
$(this).siblings('input.make').val($(this).val());
});
$('select.model').live('change', function() {
$(this).siblings('input.model').val($(this).val());
});
Sucks that it's hard-coded in there for my individual cases though. Ideally, I'd like to encapsulate all the logic in that function. So that I can just have
$('input.province').cascade('country', 'select.country', '/get-provinces.json');
$('input.make').cascade('year', 'select.year', '/get-makes.json');
$('input.model').cascade('make', 'select.make', '/get-models.json');
Yes that is possible.
$(…).change(function () { /* fn1 */ })
.change(function () { /* fn2 */ });
jQuery event binding is additive, calling .change a second time does not remove the original event handler.
Ryan is correct in jQuery being additive, although if you find there are problems because you are chaining the same event, beautiful jQuery allows another approach, and that is calling the second function within the first after completion of the first as shown below.
$('input:checkbox').change(function() {
// Do thing #1.; <-- don't forget your semi-colon here
(function() {
// Do thing #2.
});
});
I use this technique frequently with form validation, one function for checking and replacing disallowed characters input, and the second for running a regex on the results of the parent function.
Update to Post:
OK... You all are quick to beat on me with your negative scores, without understanding the difference in how we each view Mark's request. I will proceed to explain by example why my approach is the better one, as it allows for the greatest flexibility and control. I have thrown up a quick example at the link below. A picture's worth a 1000 words.
Nested Functions on One Event Trigger
This example shows how you can tie in three functions to just one change event, and also how the second and third functions can be controlled independently, even though they are still triggered by the parent change event. This also shows how programmatically the second and third functions can BOTH be tied into the same parent function trigger, yet respond either with or independently (see this by UNCHECKING the checkbox) of the parent function it is nested within.
$('#thecheckbox').change(function() {
$("#doOne").fadeIn();
if ($('#thecheckbox').attr('checked')) { doFunc2() }
else { doFunc3() };
function doFunc2() { $("#doTwo").fadeIn(); return true; }
function doFunc3() { $("#doTwo").fadeOut(); return true; }
$("#doThree").fadeIn();
});
I've included the third 'Do thing #3 in the example, to show how yet another event can follow the two nested functions as described earlier.
Forgive the earlier bad pseudocode originally posted first, as I always use ID's with my jQuery because of their ability to give everything an individual status to address with jQuery. I never use the 'input:checkbox' method in my own coding, as this relies on the 'type' attribute of an input statement, and therefore would require extra processing to isolate any desired checkbox if there is more than one checkbox in the document. Hopefully, the example will succeed at articulating what my comments here have not.
I am actually not sure exactly if you can bind two different change events. But, why not use logic to complete both events? For example...
$('input:checkbox').change(function() {
// Do thing #1.
// Do thing #2.
});
That way, you get the same benefit. Now, if there are two different things you need to do, you may need to use logic so that only one or the other thing happens, but I think you would have to do that anyway, even if you can bind two change events to the same element.

Categories

Resources