What is the best place to load the options for select2 asynchronously. I want the same facility as ajax, but instead of select2 sending an ajax request, it needs to load the values asynchronously from a promise object. Below code works, in which I load the data in query, but which means every keystroke, invocation of select dropdown, it will query the data. so, what is the correct configuration?
code:
var items2 = [
{
"Id": 1,
"Name": "First"
},
{
"Id": 2,
"Name": "Second"
},
{
"Id": 3,
"Name": "Third"
}
];
var names = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve(items2);
}, 200);
return deferred.promise;
};
var query: function (query) {
var results = [];
names().then(function(d){
$.each(d, function(index, item){
results.push({
id: item.Id,
text: item.Name
});
});
query.callback({ results: results });
})
};
Edit
looking at the source, it looks like it only allows either ajax or local for querying data. It would have been ideal if local takes a function which returns the data. Am I on the right track? is there an easy way to patch it?
thanks
// exports
window.Select2 = {
query: {
ajax: ajax,
local: local,
tags: tags
}, util: {
debounce: debounce,
markMatch: markMatch,
escapeMarkup: defaultEscapeMarkup,
stripDiacritics: stripDiacritics
}, "class": {
"abstract": AbstractSelect2,
"single": SingleSelect2,
"multi": MultiSelect2
}
};
Edit2:
'local` indeed accepts a function. but it doesn't play nicely with remote data, as the data is received with a delay (async), drop down is not populated with the new data. I have to close and open the drop-down again. This is not intuitive for the user.
As far I see, select2 invokes an ajax call for each key stroke, and opening the select box. I can get the same behavior using query (with promise) as in the original question. I was expecting select2 loads the data once, then do rest locally (search, and further invocation till any data change). looks like this is not an option. I may just cache my results locally. Better answer welcome.
Related
This question already has answers here:
Wait until all jQuery Ajax requests are done?
(22 answers)
Closed 3 years ago.
Let me just start this by saying I've done a series of searches online, but can't seem to piece it together.
Requirements: Use jQuery :(
On click, I am using a .getJSON call to get an object with several layers.
Here's an example of the data:
myObj = {
title: 'some name',
items: [
{
name: 'item-1',
url: '/item-1'
},
{
name: 'item-2',
url: '/item-4'
},
{
name: 'item-3',
url: '/item-4'
},
{
name: 'item-4',
url: '/item-4'
},
]
}
I want to loop through all of the urls, and call an .ajax operation on them, and then store the new data I get back in their respective objects.
It would look like this:
myObj = {
title: 'some name',
items: [
{
name: 'item-1',
url: '/item-1',
properties: {//whole new set of data from url}
},
{
name: 'item-2',
url: '/item-4',
properties: {//whole new set of data from url}
},
{
name: 'item-3',
url: '/item-4',
properties: {//whole new set of data from url}
},
{
name: 'item-4',
url: '/item-4',
properties: {//whole new set of data from url}
},
]
}
Once all of that is complete and each object has this new bit of data, I then want to do something with the myObj, like render it to a jquery template (ugh), but the new data MUST be inside of each item.
Here's what I have so far:
var myItems = myObj.items;
$(myItems).each(function(index, item) {
var itemUrl = '/somestuff/' + item.url + '.js';
$.getJSON(itemUrl).done(function(itemData) {
item.data = itemData;
});
}).promise().done(function() {//render data to template})
The only problem I'm having is that sometimes the data doesn't exist yet (item.properties) when the template renders, and therefore cannot render undefined.
I've tried unsuccessfully chaining .done(), and have now come across using .when(), but don't know how to write the line of code to make .when() work properly.
Any help is appreciated, and I'd be happy to clarify details.
If you capture the Promise (technically a jQuery Deferred object, actually) generated by each AJAX request, and add them to an array, then you can call .when() to execute some code once all of the Promises are resolved. Something like this (untested):
var myItems = myObj.items;
var promises = [];
$(myItems).each(function(index, item) {
var itemUrl = '/somestuff/' + item.url + '.js';
var p = $.getJSON(itemUrl);
p.then(function(itemData) {
item.data = itemData;
return itemData;
});
promises.push(p);
});
$.when.apply($, promises).then(function() { //render data to template...
This is probably preferable to chaining the done() callbacks, because it still allows the requests to execute in parallel, which is likely to be faster (although this is somewhat dependent on the server, but that's a separate issue).
I am working on a single page application, and I've so far successfully created a system that easily lets me switch between pages, now I think I need a way to call specific functions when opening a certain page.
For each page I have a function declared, for example #page1 has page1_func, #page2 has page2_func and so on.
The pages are all stored in this fashion
var appPages = {
"page1": {
"id": "#page1",
"title": "Page 1 Title",
"callback": page1_func
},
"page2": {
"id": "#page2",
"title": "Page 2 Title",
"callback": page2_func
}
};
There's another function that takes care of the navigation, which when called with fetches the information about that page i.e its title and id to update the DOM.
I also want that function to actually be able to call the functions that's in the page's callback property.
function NavigateToPage( page ) {
..
..
appPages[ page ].callback( "some args" );
}
However this isn't working as I am declaring functions at the end of the script, but as far as I know declare a function at the end or the beginning it doesn't matter, it is always available though in my case the console tells me the page1_func isn't defined.
I need help with a better solution to this problem
This is works though.
const appPages = {
page1: {
id: '#page1',
title: 'Page 1 Title',
callback: page1_fn,
},
page2: {
id: '#page2',
title: 'Page 2 Title',
callback: page2_fn,
}
};
function triggerCallback(page) {
appPages[page].callback('args...');
}
triggerCallback('page1');
function page1_fn(arg) {
document.write(arg);
}
function page2_fn(arg) {
document.write(arg);
}
I am in desperate need of some help from JavaScript experts. I have spent the last 7 hours trying hundreds of combinations of code to get a basic Tag selection input field to work with the library x-editable and select2.
I am building a Project Management app which is going to have Project Task data shown in a popup Modal Div. In the Task Modal, all Task fields will be editable with in-line edit-in-place capability using AJAX.
I am using:
the jQuery edit in place library called X-Editable - http://vitalets.github.io/x-editable/
The drop down selection jQuery library Select2 - https://select2.github.io/
The jQuery MockAjax library to allow simulating AJAX request responses. https://github.com/jakerella/jquery-mockjax
I have setup a basic JSFiddle demo to experiment with just for this StackOverflow question. I don't have the thousands of lines of code from my actual application , however I do have majority of the 3rd part libraries that are being used included into the page. THe reason is to make sure that none of them are interfering with the results.
JSFiddle Demo: http://jsfiddle.net/jasondavis/Lusbqfhs/
The Goal:
Setup X-editable and Select2 on a Field to allow users to select and enter in Tags for a Project Task.
Fetch available Tag records from a backend server which will return a JSON response with Tag ID number and Tag Name and use this data to populate the Selection2 input field to allow a user to select multiple Tags.
Allow user to also type in a NEW tag and it will post and save the new Tags to the backend as well!
When tags are selected and the save button is clicked, it will save the list of selected Tags bu ID number back to a database.
Problems:
Now I have tried hundreds of variations of options and code combinations from my research in the past 7 hours. I cannot seem to get this basic functionality to work and majority of the examples I have found do not seem to work correctly anymore!
On this demo page for the library x-editable http://vitalets.github.io/x-editable/demo-plain.html?c=inline near the bottom of the examples in the table where it says Select2 (tags mode) that functionality is what I need! I just need it to load the available tags from an AJAX request and all the docs claim it can do this with no problem at all!
This is the Documentation section from X-Editable for Select2 fields - http://vitalets.github.io/x-editable/docs.html#select2
It also links to the Select2 documentation and claims that all the Options in Select2 can be set and used as well located here - https://select2.github.io/options.html
I use MockAjax library to simulate an AJAX response in pages like JSFiddle for testing. In my JSFiddle demo here http://jsfiddle.net/jasondavis/Lusbqfhs/ I have 2 MockAjax responses set up....
$.mockjax({
url: '/getTaskTags',
responseTime: 400,
response: function(settings) {
this.responseText = [
{id: 1, text: 'user1'},
{id: 2, text: 'user2'},
{id: 3, text: 'user3'},
{id: 4, text: 'user4'},
{id: 5, text: 'user5'},
{id: 6, text: 'user6'}
];
}
});
$.mockjax({
url: '/getTaskTagById',
responseTime: 400,
response: function(settings) {
this.responseText = [
{id: 1, text: 'user1'},
{id: 2, text: 'user2'},
{id: 3, text: 'user3'},
{id: 4, text: 'user4'},
{id: 5, text: 'user5'},
{id: 6, text: 'user6'}
];
}
});
They both are supposed to return a Mock JSON string for my Selection list to be populated with.
Here is my code for the demo...
$(function(){
//remote source (advanced)
$('#task-tags').editable({
mode: 'inline',
select2: {
width: '192px',
placeholder: 'Select Country',
allowClear: true,
//minimumInputLength: 1,
id: function (item) {
return item.CountryId;
},
// Get list of Tags from AJAX request
ajax: {
url: '/getTaskTags',
type: 'post',
dataType: 'json',
data: function (term, page) {
return { query: term };
},
results: function (data, page) {
return { results: data };
}
},
formatResult: function (item) {
return item.TagName;
},
formatSelection: function (item) {
return item.TagName;
},
initSelection: function (element, callback) {
return $.get('/getTaskTagById', {
query: element.val()
}, function (data) {
callback(data);
});
}
}
});
});
Now in the demo when you click the field to select Tags, it just keeps "loading" and never gets a result. Looking at the Console, it seems my MockAjax request is not working, however the 2nd one is working so I am not sure what is wrong with my AJAX request?
I could really use some help if someone can help to get this to work I would be very greatful, I have spent my whole night without sleep and am not even any closer to a working solution! Please help me!
Thank you
X-Editable uses Select2 3.5.2 which doesn't use jQuery.ajax() directly. It has its own ajax function and calls jQuery.ajax() like that:
transport = options.transport || $.fn.select2.ajaxDefaults.transport
...
handler = transport.call(self, params);
That's why $.mockjax({url: '/getTaskTags'... does not work.
To get it work you need to create your own transport function, something like that:
var transport = function (queryParams) {
return $.ajax(queryParams);
};
and set the transport option:
ajax: {
url: '/getTaskTags',
=> transport: transport,
type: 'post',
dataType: 'json',
data: function (term, page) {
return { query: term };
},
results: function (data, page) {
return { results: data };
}
},
I'm using twitter's typeahead.js to show an autocomplete.
My code is as follows:
$.ajax({
url:'/getlocals/',
success: function(data){
$('.local-type').typeahead({
name: 'local-tt',
source: data,
suggestion: Handlebars.compile('<p><strong>{{locality}}</strong> – {{city}}</p>')
})
}
});
Where data is a json response from /getlocals/ and has the following format:
[{
"locality": "Powai",
"city": "Mumbai",
}, {
"locality": "Colaba",
"city": "Mumbai",
}, {
"locality": "Andheri East",
"city": "Mumbai",
}, {
"locality": "Andheri West",
"city": "Mumbai",
}]
This however doesn't work and I have a feeling that the source option is not receiving the correct format. What sort of format is typeahead looking for and how do I provide it?
EDIT:
I've also used part of the solution provided here: Twitter Bootstrap Typeahead - Id & Label but this doesnt seem to work for me.
As far as I can tell, reading the documentation, you did a few things wrong with this plugin;
First, your plugin usage is incorrect; options are specified in one object, and data-sets in another:
$('.local-type').typeahead({/*options*/}, {/*data set1*/}, {/*optional data set 2*/});
In the case of your example, it'd be something like this:
$('.local-type').typeahead({
name: 'local-tt',
}, {
source: function (query, cb) {
cb(data);
},
templates: {
suggestion: Handlebars.compile('<p><strong>{{locality}}</strong> – {{city}}</p>')
}
});
To explain the above, the 'suggestion' key is a child of the 'templates' key, it does not exist stand-alone. The 'source' key is a function, not an array. The function is passed the query, and the 'callback' function, which is then executed with the data as the parameter.
Secondly, your method of creating an instance of the typeahead plugin, within an ajax call, is not preferred assuming that ajax call is executed more than one time. If it is not, then disregard the following suggestion, which is to put the ajax call -within- the typeahead plugin creation, as such:
$('.local-type').typeahead({
name: 'local-tt',
}, {
source: function (query, cb) {
$.ajax({
url:'/getlocals/',
success: function(data){
cb(data);
}
});
},
templates: {
empty: [
'<div class="empty-message">',
'unable to find any Best Picture winners that match the current query',
'</div>'].join('\n'),
suggestion: Handlebars.compile('<p><strong>{{locality}}</strong> – {{city}}</p>')
}
});
This is of course assuming the ajax request is made more than once; if it is not, disregard the above.
Basic demo
I would like to dynamically load select elements from an API request.
Here is my controller:
var myApp = angular.module('myApp',[]).controller('tripCtrl', function($scope){
//Call to API to get people
$scope.people = [
{
"id": 1,
"name": "Joe Hamlet"
},
{
"id": 2,
"name": "Mary Jane"
},
{
"id": 3,
"name": "Tom Lee"
}
];
//Call to API to get the element to load
$scope.selectElement =
{
"Options": "person[dynamicValue] as person[dynamicDisplayName] for person in people",
"DisplayName": "name",
"Value": "id"
};
//Dynamicly load properties
$scope.dynamicValue = $scope.selectElement.DisplayName;
$scope.dynamicDisplayName = $scope.selectElement.Value;
});
HTML:
<select ng-model="selectedPerson" ng-options="{{selectElement.Options}}">
<option value="">Select</option>
</select>
{{selectedPerson}}
I created a JSFiddle trying to accomplish this. http://jsfiddle.net/HB7LU/9493/
I found this question which I was able to implement, but when I tried to set the ng-options from the Element's Options property, it failed to load. When inspected the HTML the code looks to be set properly, but the model binding isn't working.
Edit 12/28/2014:
After updating the Angular version in the original JS Fiddle, it worked properly, however when I expanded to use an actually API, I found another issue with loading ng-options dynamically. Here is the more in depth JS Fiddle: http://jsfiddle.net/zjFp4/330/
Also here is my updated controller. The dataService.getElement() calls a hard coded string, where as the dataService.getElementFromApi() calls the same exact string, just from json-generator (which is the mock API). When inspected the objects set from the API, everything is there, so it must be an issue with the binding in Angular. Any ideas on how to fix this?
function tripCtrl($scope, dataService) {
//Call to API to get people
dataService.getPeople().then(
function (event) {
$scope.people = event;
},
function (s) {
console.log(s); }
);
//Call to API to get the element to load
$scope.selectElement = dataService.getElement();
dataService.getElementFromApi().then(
function (event) {
$scope.apiElement = event;
$scope.dynamicValue = $scope.apiElement.Value;
$scope.dynamicDisplayName = $scope.apiElement.DisplayName;
},
function (s) {
console.log(s); }
);
}