I builded a JQuery component to make a dropdown-accordion element. Each time i click on a item in a accordion, it will save the selection, close this accordion andopen the next one if exists.
To add data, it work as it :
$(window).ready(function () {
$(selector).data('dropdownaccordion').enqueueAccordion({ // Ajax parameter
name: "Commune",
data: {
url: "/trends/list",
method: 'GET',
datatype: "json",
timeout: 5000
}
},
{
name: "Batiment",
data: {
url: "/trends/list/"+this.path[0], // this.path[0] refer to the first selection, as this.path[1] will refer to the second etc
method: 'GET',
datatype: "json",
timeout: 5000
}
},
{ //Note that we can pass array instead of Ajax parameter
name: "Dummy",
data: ["One", "Two", "Three"]
})
});
I designed the component to work good with Rest API.
The problem is exposed for the second element i added. As you can see, i'm trying to pass this.path[0], refering to the first item selected. But the property path doesn't exist in the $(windows).ready scope, but in the $(selector).data('dropdownaccordion') scope.
I can replace it by $(selector).data('dropdownaccordion').path[0], but i want something more global, like ExpressJS do in URL (http://www.dummy.com/path/:item1/:item2 and replacing :itemx by the user selection)
Do you know a better solution, and if not, do you know a way to make it good ?
EDIT
My Component is designed as follow :
+function ($) {
'use strict';
/**
* Dropdown_Accordion CLASS DEFINITION
*/
function DropdownAccordion(component, ...accordions) {
this.accordions = [];
for(var i=0;i<accordions.length;i++){
this.accordions.push(accordions[i])
}
...
this.component = $(component);
...
// Render the accordions as DOM
this.render.call(this);
//Events
this.component
.on("click", ".accordion-item-body", $.proxy(function(event){
this.path.push($(event.target).text())
this.component.trigger('click.selection.dropdownaccordion', [$(event.target)])
this.openNext(event);
}, this))
.on('show.bs.dropdown', $.proxy(this.showMenu, this))
.on('shown.bs.dropdown', $.proxy(this.openNext, this))
.on('hide.bs.dropdown', $.proxy(function (event) {
this.component.find('.dropdown-menu').first().stop(true, true).slideUp();
this.component.find(".accordion-item .panel-collapse.collapse").each(function(){
$(this).collapse('hide');
});
this.opened = null;
(this.path.length !== this.accordions.length) ? this.component.trigger("abort.selection.dropdownaccordion", [this.path]):null
this.path = []
}, this));
}
DropdownAccordion.prototype = {
constructor: DropdownAccordion,
// Collapse accordion every time dropdown is shown
showMenu: function () {
// show the accordion
},
openNext: function (clickEvent) {
// Open the next accordion
},
render: function () {
// Will render the dropdown-accordion
// The following is how the AJAX request is performed
...
// When the accordion is opened, make the ajax request
DOMaccordion.on("open.accordion.dropdownaccordion", function () {
// The DOM element contains in data the options for the ajax request, or nothing if the accordion is an array
**$.ajax($(this).data('content.accordion.dropdownaccordion'))**
});
...
}
},
enqueueAccordion: function (...accordions) {
for(var i=0; i<accordions.length;i++)
this.accordions.push(accordions[i]);
this.render()
},
dequeueAccordion: function (name) {
this.accordions = this.accordions.filter(function (obj) {
return obj.name !== name;
})
this.render()
}
}
Look at the ** ** in the code, this is where the Ajax options is stored. I want that when i click on a item, there is a substitution on the .url to include the first item clicked.
I hope this seems clearer to you.
You can let enqueueAccordion accept a callback and pass the current accordion as a parameter:
DropdownAccordion.prototype = {
// ...
enqueueAccordion: function (makeAccordions) {
var accordions = makeAccordions(this);
for (var i = 0; i < accordions.length; i++)
this.accordions.push(accordions[i]);
this.render();
}
}
$(selector).data('dropdownaccordion').enqueueAccordion(function (_this) {
return [
{
name: "Batiment",
data: {
url: "/trends/list/" + _this.path[0],
method: 'GET',
datatype: "json",
timeout: 5000
}
];
});
If you want to use this directly, you can temporarily attach the callback to the current accordion:
DropdownAccordion.prototype = {
// ...
enqueueAccordion: function (makeAccordions) {
this.makeAccordions = makeAccordions;
var accordions = this.makeAccordions();
delete this.makeAccordions;
for (var i = 0; i < accordions.length; i++)
this.accordions.push(accordions[i]);
this.render();
}
}
$(selector).data('dropdownaccordion').enqueueAccordion(function () {
return [ // Ajax parameters
{
name: "Batiment",
data: {
url: "/trends/list/" + this.path[0],
method: 'GET',
datatype: "json",
timeout: 5000
}
}
];
});
I dug through the expressjs code, and found how they do the URL substituion. It work with the path-to-regex module. It seems not to be developped for browserside so i had to found another solution.
#aaron inspired me, and i finally add the possibility to have a callback in place of the url :
{
name: "Capteur",
data: {
url: function (path) {
return "/trends/list/" + path[0] + "/" + path[1]
},
method: 'GET',
datatype: "json",
timeout: 5000
}
}
By doing this, people are now able to modify the path based on the previous selection.
Related
I am doing some experiment using select2 js plugin inside a Vue js component, my mount() function looks like this
let self = this;
let url = self.context.recipes_autocomplete_url;
$(this.$refs.recipe_selection).select2({
minimumInputLength: 2,
ajax: {
dataType: 'json',
delay: 250,
cache: true,
url: function (params) {
return url;
},
data: function (params) {
let query = {
term: params.term
}
return query;
},
processResults: function (data, params) {
let more = (data.next != null)
if (more)
url = data.next;
else
url = self.context.recipes_autocomplete_url;
return {
results: data.results,
pagination: {
more: true
}
};
}
}
}).on("change.select2", function (e) {
let data = $(self.$refs.recipe_selection).select2('data')
if (data.length == 0)
return
// more staff here
});
My problem is with pagination, even if I hard code pagination:{more: true} the call to the next page never happen, I know this is not a common usage, but I am working on a legacy code, any idea?
I am using the last version of select2.js
I have two buttons that both performs AJAX call:
$("#save").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
The requirement is that when the user click the finalize button, the system will first perform a save before actually tagging it as final. To reuse the code attached to the save button, I call the onclick listener of the save button before the actual AJAX call like this:
$("#tag-as-final").click(function() {
$("#save").click()
^^^^^^^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
But it will not do "save-and-finalize-after" behavior since both AJAX calls are asynchronous. I need to run one after another, but cannot afford to make the AJAX call of the save button synchronous (I'm doing also a lot of other things while the tagging occurs occurs). I know this would be silly but I'm thinking something similar to...
$("#tag-as-final").click(function() {
$("#save").click().peformAsyc()
^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
...that will force it to finish performing first the chained function before continuing, but I know that is not available. Is there any way to do this? My current work-around is placing the same save AJAX function inside the finalize AJAX function, though it doesn't allow me to code DRY (Don't Repeat Yourself):
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
},
error: function(r) {
...
}
})
})
It's pretty simple, you are better using jquery "promises". Like so:
var generalSettings = { }; //Settings for AJAX call.
var jqXHR = $.ajax(generalSettings); //Do AJAX call.
generalSettings.data = 'newdata'; //update generalSettings
jqXHR.done(function(data){
$.ajax(generalSettings); //New Petition with updated settings.
});
This is using ES6 promises and jQuery promises:
function doAjaxAsPromise(settings){
return new Promise(function(resolve){
var jqXHR = $.ajax(settings);
jqXHR.done(function(data){
resolve(data);
});
});
}
var settings = { };
var petition = doAjaxAsPromise(settings);
var secondpetition = petition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
var thirdpetition = secondpetition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
//If needed to reuse settings object outside promise scope:
//var settings = Object.create(settings);
Some other nice thing you can do for code reuse:
function save(settings) {
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//do something with your data.
});
}
function tagAsFinal(savedPromise, settings){
return savedPromised.then(function(){
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//work with data;
});
});
}
$('save').on('click', function(){
save(settings); //settings = $.ajax settings.
});
$('tagAsFinal').on('click', function(){
var generalSettings = { };
var settingsone = Object.create(generalSettings);
var settingstwo = Object.create(generalSettings);
var saved = save(settingsone); //$.ajax settings.
tagAsFinal(saved, settingstwo);
});
//Can still be reduced.
I use Select2-4.0.0 I have been struggling with this problem for a whole day that to update the data.
I search every posts I can like Update select2 data without rebuilding the control, but none have them work.
What I need is really simple (a setter to data):
I have a diaglog, which had a select. Every time I open the diaglog, I will ajax for an data to keep it in a local array like:
when dialog open :
var select2List=syncToLoadTheData();//data for select2
$('#search-user-select').select2({ //here when secondly executed, the select2's data on UI does not refreshed
data:select2List,
matcher: function(params, data){
var key = params.term;
if ($.trim(key) === '') {
return data;
}
if( (matchKeyAndPinyin(key,data.text))){
return data;
}
return null;
}
}
But the problem is even though the list is changing, the select options does not change at all.Please note in my test case, every time i open the dialog, the data from server is changed:
What I had tried:
1.when init:
data: function() { return {results: select2List}; }// not work to show any data at all
2.when secondly open dialog:
$( "#search-user-select").select2('data',newdata,true);//not work to have the new data
3.when secondly open:
$("#search-user-select").select2("updateResults");//Error, does not have this method
And some other method like directly change the array's data(only one copy of the data), but none of them work.
I had the same problem before, my problem was i need to update the select2 after every ajax request with new data.
and this how i fixed my code.
EventId = $(this).attr('id');
$.ajax({
url: 'AjaxGetAllEventPerons',
type: 'POST',
dataType: 'json',
data: {id: EventId},
})
.done(function(data) {
$("#select2_job").select2({
minimumInputLength: 2,
multiple:true,
initSelection : function (element, callback) {
//var data = data;
callback(data);
},
ajax: {
url: "/AjaxGetAllPersonForEvent",
dataType: 'json',
quietMillis: 100,
data: function (term) {
return {
term: term
};
},
results: function (data) {
var myResults = [];
$.each(data, function (index, item) {
myResults.push({
'id': item.id,
'text': item.fullname
});
});
return {
results: myResults
};
}
}
});
I hope this example will help you to solve your problem
I have a select in my html and would like to add the options via ajax when the page loads. The options values are in my DB and I get them via a call to ajax. To do so, I'm writing a class in javascript but I just can't get my data when it runs. Please take a look :
--- Main.js ---
function MyLoader() {
this._clients = null;
this._code = null;
}
Loader.prototype = {
var context = this;
loadClients: function() {
$.ajax({
url: "my/php/",
type: "POST",
data: {...},
success: function(response) {
context._clients = response;
}
});
},
getCode: function() {...}
};
Then I have the following :
$(document).ready(function() {
var loader = new Loader();
loader.loadClients();
alert(loader._clients);
//Here I want to add my options to the select
});
My alert always returns null, and I don't understand why. I need to save my data in the class in order to access them anytime I need to.
Can you point me to the right direction to make all my stuff work ? Thank you for your answers.
Loader.prototype = { // v---callback parameter
loadClients: function(callback) {
$.ajax({
url: "my/php/",
context: this, // <---set success context
type: "POST",
data: {...},
success: callback // <---pass callback
});
},
getCode: function() {...}
};
$(document).ready(function() {
var loader = new Loader();
// v---pass callback
loader.loadClients(function(response) {
this._clients = response;
alert(this._clients);
//Here I want to add my options to the select
});
});
I believe that you need to do all of your dynamic loading inside of your 'success' callback, since it is loading asynchronously.
You need to do it inside the success callback since it's async:
Loader.prototype = {
var context = this;
loadClients: function() {
$.ajax({
url: "my/php/",
type: "POST",
data: {...},
success: function(response) {
context._clients = response;
alert(loader._clients);
//Here I want to add my options to the select
}
});
},
getCode: function() {...}
};
$(document).ready(function() {
var loader = new Loader();
loader.loadClients();
});
I'm a newbee about jQuery's workflow and I would like to setup a javascript class that uses an internal method to make an AJAX request. When the request returns with success, the jQuery AJAX callback should invoke a method owned by the class itself. That's the code:
function IXClock()
{
this.m_intervalID = 0;
this.startClock = function ()
{
this.m_intervalID = setInterval(this.tictac, 500);
}
this.stopClock = function ()
{
clearInterval(this.m_intervalID);
}
this.setClockTime = function(p_strTime)
{
$('#clock').html(p_strTime);
}
this.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
this.setClockTime(data);
}
});
}
}
The class represents a clock, with an internal method (tictac) that requests "what's the time" on the server side.
After the server says the time, the jQuery's AJAX method should invoke the setClockTime method of the IXClock class. The invoke method will update the #clock div item in the html page.
The problem is that the method this.setClockTime() results unknown and the javascript return the "this.setClockTime is not a function" error.
The question is: is there a way to invoka a class method from the jQuery's AJAX callback ?
I think that the problem is that the this in your callback function is different from the this referring to IXClock. Try:
var thisClass = this ;
this.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
thisClass.setClockTime(data);
}
});
}
Test Case (added to site which already has jQuery loaded):
function uClass () {
this.testFunction = function(input) {
alert(input) ;
}
this.ajaxFunction = function() {
var myClass = this ;
$.ajax({
type: 'POST',
url: '/',
complete: function(data) {
alert(myClass.testFunction) ;
myClass.testFunction(data) ;
this.testFunction(data) ;
}
}) ;
}
}
var k = new uClass() ;
k.ajaxFunction() ;
It happens bacause your callback function leave in global context.
You can choose 2 ways
Use .bind function to bind context to callback function http://www.robertsosinski.com/2009/04/28/binding-scope-in-javascript/
jQuery's AJAX supports transfer some data to callback function. You can write smth like this:
:
this.tictac = function () { $.ajax ({ type: 'POST', context:this, url: '/rap/rapClock.php', complete: function (data) { this.setClockTime(data); } }); }
}
this does not refer to IXClock in your ajax callback. this allways points to the current scope (have a look at this document). You need to do something like this:
this.prototype.tictac = function ()
{
var self = this;
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
self.setClockTime(data);
}
});
}
You can also use jQuery's .proxy()-function for this purpose:
this.prototype.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: $.proxy(function (data) {
this.setClockTime(data);
}, this)
});
}
The this in the result handler is not what you expect it is. (It is not the IXClock instance)
function IXClock()
{
this.m_intervalID = 0;
}
IXClock.prototype = {
startClock: function ()
{
this.m_intervalID = setInterval(this.tictac, 500);
},
stopClock: function ()
{
clearInterval(this.m_intervalID);
},
setClockTime: function(p_strTime)
{
$('#clock').html(p_strTime);
},
tictac: function ()
{
var that = this;
$.ajax({
type: 'POST',
url: '/rap/rapClock.php',
success: function (data) { // You want success here, not complete, IMO
that.setClockTime(data);
}
});
}
}
If you ask me, that ajax call is doing evil. It does not seem to send any data, nor modify any
state on the server, but is expecting/getting/using data from the php, yet is using the POST method.
Should've been
$.get('/rap/rapClock.php', function (data) {
that.setClockTime(data);
});
One simple solution is, to keep your callback function as self = this. This will support inheritance also.
class Record{
get_data(){
self = this;
$.ajax({
type : "GET",
url : "/get_url",
dataType : "json",
contentType: "application/json; charset=utf-8",
data : {},
success : function(data){
console.log(data);
self.load_table(data);
},
});
}
static load_table(data){
console.log(data);
}