I am new to javascript, callback functions and select2. Thanks in advance for your help :)
I am looking at implementing select2 to search against an API but I will have to use axios instead of the default jQuery method. Below is my code. I am able to send and retrieve results but I am not sure how to use the success callback.
I get "TypeError: success is not a function"
$("#profile-select").select2({
ajax: {
transport: function(params, success, failure){
axios.post("/rest/vue/1.0/profile/search", {query: $("#profile-select").val()})
.then(function(response){
success(response);
})
.catch(function(error){
alert(error);
});
},
processResults: function(data){
var processedArray = [];
data.profiles.forEach(function(item){
processedArray.push({id: item.ID, text: item.name});
});
return processedArray;
}
},
minimumInputLength: 2,
placeholder: "Select a profile",
allowClear: true
});
Questions
How do I return the response data to processResults in the .then function on the axios request? The documentation is at https://select2.org/data-sources/ajax
What is the best way to pass the input from the select list to the post request? Currently I am using jQuery.val() function which doesn't seem to work.
You can create callback functions just like any other functions.
For example:
function success(response) {
//do with response data what's necessary
}
Callback means that you pass this function as parameter for later execution.
When you create select2 ajax transport, then you pass your function name as parameter (as callback function).
When code execution meets line "success(response);" then your success function is actually executed.
This is my example using laravel as backend. The param success in transport is a callback for processResults method. Use data function to get the value from the input.
{
// [..]
ajax: {
url : 'yourUrl',
data: function (params) {
let query = {
term: params.term, // Get value form input
page: params.page || 1
}
return query;
},
transport: function(params, success, failure){
axios.get(this.url, { params : this.data } )
.then(function(data){
success(data);
})
.catch(function(error){
console.log(error);
});
},
processResults: function (response, params) {
// params.page = params.page || 1;
return {
results: response.data.data,
pagination: {
more: response.data.links.next ? true : false,
}
};
},
},
}
Related
My application has a lot of AJAX calls, each of them return a JSON response. Instead of validating the data in each of the the .done() calls, I'm trying compact the code.
What we have so far
$.ajax({
url: 'test',
type: 'GET',
data: {
_token: token
},
dataFilter: function(jsonResponse) {
return isValidJson(jsonResponse);
}
}).done(function(jsonResponse) {
// do things
});
isValidJson(jsonResponse) {
try {
var parsedJson = $.parseJSON(jsonResponse);
if (parsedJson.error == 1) {
notificationController.handleNotification(parsedJson.message, 'error');
return false;
}
} catch (err) {
notificationController.handleNotification('A server-side error occured. Try refreshing if the problem persists.', 'error');
return false;
}
return jsonResponse; // Have to return the original data not true
}
The expected behavior is that if dataFilter returns false, it will trigger .fail(), if it returns true then it will continue to .done(). Instead, it just continues to .done() with the result of isValidJson().
Is there also a way to make .fail() do something standard like send a notification to the user without having to put it under every AJAX call?
Easiest way is to create a shorthand for $.ajax, by extending it.
Extending the AJAX call
jQuery.extend({
myAjax: function(params){
// Here we can modify the parameters and override them e.g. making 'error:' do something different
// If we want to add a default 'error:' callback
params.error = function() {
console.log('its failed');
};
// or you can specify data parse here
if (params.success && typeof params.success == 'function') {
var successCallback = params.success;
var ourCallback = function(responseJson) {
if (isValidJson(responseJson)) { // Validate the data
console.log('The json is valid');
successCallback(responseJson); // Continue to function
} else {
console.log('The json is not valid');
}
}
params.success = ourCallback;
}
return $.ajax(params);
}
});
Now everytime you want to make an AJAX call in your application, you DO NOT use $.ajax({}). Instead, you use $.myAjax({});
Example
$.myAjax({
url: 'domain.com',
type: 'GET',
success: function(data) {
// Do what you'd do normally, the data here is definitely JSON.
},
error: function(data) {}
});
And this special function will handle all errors same way, no need to write those validators every time.
Try to do it like this (Not tested):
var jxhr = $.ajax({
url: 'test',
type: 'GET',
data: {
_token: token
},
dataFilter: function(jsonResponse) {
if (!isValidJson(jsonResponse)) {
jxhr.abort();
}
return jsonResponse;
}
}).done(function(jsonResponse) {
// do things
});
By using this strategy - you are violating "separation of concern" strategy.
Ajax should resolve or reject according to its action. Not according if response is JSON or not.
A possible solution : ( sure there are also another solutions)
function GetSanitized(d) {
return d.then(function(a) {
if (a.indexOf('{') > -1) //check if json ( just for example)
return $.Deferred().resolve(JSON.parse(a)); //return object
else
return $.Deferred().reject(a); //reject
},
function() {
return $.Deferred().reject("ajax error"); //ajax failed
}
);
}
var ajax = $.Deferred();
GetSanitized(ajax) .then(function (a){alert(" Json p's value is "+a["p"]);},function (a){alert("Error"+a);});
ajax.resolve("{\"p\":2}"); //simulate ajax ok , valid json
//ajax.resolve("\"p\":2}"); //simulate ajax ok , invalid json
//ajax.reject("\"p\":2}"); //simulate ajax bad , valid json
http://jsbin.com/vozoqonuda/2/edit
I know that the above can be achieved by using quietMillis in the AJAX call, but I am using query to cache the data. And it is here I am not able to delay the AJAX call. Below is the code
$('#AssetType').select2({
cacheDataSource: [],
placeholder: ' ',
quietMillis: 3000,
query: function q(query) {
self = this;
var key = query.term;
var cacheData = self.cacheDataSource[key];
if (cacheData) {
query.callback({
results: $.map(cacheData, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
return;
}
else {
$.ajax({
url: 'http://localhost:52377/api/reference/asset/types/' + key,
dataType: 'json',
type: 'GET',
quietMillis: 3000,
//data: function (query) {
// return { assetType: query.term, };
//},
success: function (data) {
self.cacheDataSource[key] = data;
query.callback({
results: $.map(data, function (item) {
return {
text: item.LongDescription,
id: item.AssetTypeID
}
})
});
},
cache: true
})
}
}
});
Is there any work around to delay the AJAX call so that the AJAX call is not fired for every keystroke?? The reason for using "query" is for caching, which is not achieved just by setting cache to true in the AJAX call.
According to the select2 documentation, you can do this easily.
A request is being triggered on every key stroke, can I delay this?
By default, Select2 will trigger a new AJAX request whenever the user changes their search term. You can set a time limit for debouncing requests using the ajax.delay option.
This will tell Select2 to wait 250 milliseconds before sending the request out to your API.
$('select').select2({
ajax: {
url: '/example/api',
delay: 250
}
});
I found a way to delay the triggering. I've used an implementation of debounce function in underscore.js . The code would now look like this
query: debounce(function q(query) {..
.....
}, 350),
Hope it helps someone.
Select2 (4.0.3) has an undocumented option: minimumInputLength
This option will prompt the user to fill in the minimum number of characters and then fire the selection
I have written a small Jquery plugin that makes it easy for me to implement Facebook like "likes" on any item in my application. My only issue now is that I struggle to implement the success / error callback of my plugin.
$('.user-like').like({
success: function(data, textStatus, jqXHR) {
$(this).text('Liked');
}
});
My issue with the above code is this line:
$(this).text('Liked');
I'm aware of what why the issue happens, I just can't find a good way to make it work like I want it. Let me explain how the script works and what my Goal is:
As you can see I'm passing the call along to the likeApi() function that executes an AJAX call. Further you see that I merge my Options with the defaults and that you can override the success and error callback of the AJAX object.
The issue is now that this in the above code is the scope of the AJAX call and not my original method. I want to allow the user to define his own success / error callback that depends on the result of the API call and allows me to do something based on the state if it was a success or failure so that I can change the like text for example. How can I do this?
(function ($) {
$.likeApi = function (action, options) {
if (action != 'like' && action != 'unlike') {
return false;
}
var options = jQuery.extend({}, jQuery.likeApi.defaults, options);
$.ajax({
type: "POST",
url: options.baseUrl + action + '.json',
data: {
data: {
Like: {
foreign_key: options.id,
model: options.model
}
}
},
success: options.success,
error: options.error,
dataType: 'json'
});
};
$.fn.like = function (options) {
var scopedOptions = options;
this.on('click', function(event) {
event.preventDefault();
$.likeApi('like', $.extend({}, scopedOptions,{
'id': $(event.target).data('like-fk'),
'model': $(event.target).data('like-model')
}));
});
return this;
};
$.fn.unlike = function (options) {
var scopedOptions = options;
this.on('click', function(event) {
event.preventDefault();
var result = $.likeApi('unlike', $.extend({}, scopedOptions,{
'id': $(event.target).data('like-fk'),
'model': $(event.target).data('like-model')
}));
alert(result);
});
return this;
};
$.likeApi.defaults = {
baseUrl: '/likes/likes/',
action: null,
model: null,
id: null,
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus);
},
success: function(data, textStatus, jqXHR) {
alert(textStatus);
}
};
}(jQuery));
Two options: you can maintain context by adding a variable that references the original this, or you can use jquery.proxy()
Option 1:
Maintain the context by adding a variable that references the original this like so:
(function ($) {
$.likeApi = function (action, options) {
var self = this;
Then you just call self whenever you are out of context.
If you want to keep self available externally, you can inject it using jquery extend.
http://api.jquery.com/jquery.extend/
options.success = $.extend(options.sucesss, {el: self});
inside your ajax call
$('.user-like').like({
success: function(data, textStatus, jqXHR) {
$(data.el).text('Liked');
}
});
Option 2:
Alternatively, you can use jQuery.proxy()
http://api.jquery.com/jquery.proxy/
proxy can change the this context for you...
I am having one of those ajax asynchronous problems. I have this function which accepts two parameters to send data to the server by jquery ajax. I know this can be done by using promises and callback functions but this is my specific problem.
function fillLightbox(id, text, callback)
{
// get json request of studyunit details
$.ajax(
{
type: 'GET',
url: 'updateExam',
data:
{
get_details: true,
exam_code: text,
event_id: id
},
dataType: 'json',
success: callback
});
}
That is my ajax request, then I have this function for an API:
scheduler.createGridView(
{
name:"grid",
fields:
[
{id:"text", label:'Unit Code', sort:'str', width:200},
{id:"date", label:'Date', sort:'date', width:'*'},
{id:"exam-title", label:'Title', sort:'str', width:'*',
template: function(start, end, ev)
{
fillLightbox(ev.id, ev.text, function(data)
{
var title = data.title;
// i can get my data here..
});
return title; // how can I do this?
}
}
]
});
My question is how can I modify the template function available in this API to be able to return the property. i.e. can I somehow pass a callback function there as well?
Please do not suggest async: false (This obviously works but lags on the browser)
EDIT: more details about Create Grid View template are found here: http://docs.dhtmlx.com/scheduler/grid_view.html#datatemplates
Thanks for your help
If I have to leverage niceties of jQuery AJAX API and set my own custom settings for each ajax call my app makes like below:
Say I have a page which displays employee information within table by making ajax calls to some API.
define(["jQuery"], function($) {
var infoTable = function (options) {
function init() {
// Provide success callback
options.success_callback = "renderData";
getData();
}
function renderData() {
// This callback function won't be called as it is not
// in global scope and instead $.ajax will try to look
// for function named 'renderData' in global scope.
// How do I pass callbacks defined within requirejs define blocks?
}
function getData() {
$.ajax({
url: options.apiURL,
dataType: options.format,
data: {
format: options.format,
APIKey: options.APIKey,
source: options.source,
sourceData: options.sourceData,
count: options.count,
authMode: options.authMode
},
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.success_callback,
error: options.error_callback,
timeout: options.timeout
});
}
}
return {
init: init
}
}
How do I achieve this?
I know we can use JSONP request as require calls but that restricts me to using jsonp, making GET requests and all other features $.ajax offers.
This example would let you either use a default success callback, or provide an override, using:
success: options.successCallback || renderData
(The example uses jsfiddle rest URLs - this fact is unimportant, and stripped out the data object to keep the example short)
define("mymodule", ["jquery"], function($) {
function renderData() {
console.log("inside callback");
}
function getData(options) {
$.ajax({
url: options.apiURL,
dataType: options.format,
method: options.method,
jsonpCallback: options.jsonpCallback,
success: options.successCallback || renderData,
error: null,
timeout: options.timeout
});
}
return {
getData: getData
}
});
require(["mymodule"], function(m) {
console.log(m, m.getData({
apiURL: "/echo/json/"
}));
console.log(m, m.getData({
successCallback: function() { console.log("outside callback"); },
apiURL: "/echo/json/"
}));
});
Would print:
GET http://fiddle.jshell.net/echo/json/ 200 OK 263ms
Object { getData=getData()} undefined
GET http://fiddle.jshell.net/echo/json/ 200 OK 160ms
Object { getData=getData()} undefined
inside callback
outside callback