I'm using jQuery and tabulator to create a fairly simple webpage showing the progress of various live events. I have three table elements on the page showing events in different states. Broadly speaking I have a table for events that are 'Live', 'Finished' and 'Upcoming'.
The tabulator code for each table looks a bit like this:
var eventTable = {
layout:"fitColumns",
columns:[
{title:"Event ID", field:"eventID"},
{title:"Event name", field:"eventName"},
{title:"Event status", field:"eventStatus"}
]
};
I can then generate and populate the tables using data retrieved from the server using an AJAX call:
$('#live-events-table').tabulator(eventTable);
$('#live-events-table').tabulator("setData", "/cgi-bin/getevents.py");
$('#live-events-table').tabulator("setFilter", "status", "in", ["intermission", "mid-event"]);
Then similar code for the finished and upcoming tables.
This creates three requests for getevents.py every time the page is updated. This is undesirable because the data is pulled from another API and I want to avoid sending multiple identical requests. Also, there's a (small) chance the data could change between requests.
Is there a way of using the same data to populate all three tables?
Learnt something today. Never heard of Tabulator. I've been using DataTables for this sort of advanced tabular layout in the form of a jQuery plugin. Tabulator looks promising. Will have to explore more in the future.
In the docs I see Tabulator - Set Table Data, lots of alternative options available to set the data.
Hope I am understanding the problem correctly.
My thoughts/flow for tacking this problem would be:
Assuming your doing this already: Prep server side (getevents.py) needs to return all 'Live', 'Finished' and 'Upcoming' rows in JSON together in 1 response. How do I make a JSON object with multiple arrays?.
Client side on doc ready(), makes a jQuery ajax call to fetch this data before 3 x tabulators are built/called.
You can use a loading indicator. So the client sees something is happening in the 3 x before replaced by jQuery tabulator once the data is returned and you built with tabulator.
In the ajax success callback function you can iterate over the returned json object (containing the 3 x eventTypes). You might have to JSON.parse() if you store it in a JavaScript variable to iterate over.
Now get your specific array of event types nested objects arrays/json.
Then call your tabulator creation methods using the setData pointing to each specific JavaScript array containing the relevant data.
Not sure if this falls under "premature optimization", but I would proceed as the problem itself is rather fun to solve, but you know the requirements/needs better than I.
Good luck.
I fixed this fairly simply, using suggestions from #RoryGS.
Define the table options in a variable, as before:
var eventTable = {
layout:"fitColumns",
columns:[
{title:"Event ID", field:"eventID"},
{title:"Event name", field:"eventName"},
{title:"Event status", field:"eventStatus"}
]
};
Then make a jQuery ajax call to fetch the data, and build the table(s) in the success option of the function:
$(function() {
$.ajax({
dataType: "json",
url: "/cgi-bin/getevents.py",
success: function(data, status){
$('#live-events-table').tabulator(eventTable);
$('#finished-events-table').tabulator(eventTable);
$('#live-events-table').tabulator("setData", data);
$('#live-events-table').tabulator("setFilter", "status", "in", ["intermission", "mid-event"]);
$('#finished-events-table').tabulator("setData", data);
$('#finished-events-table').tabulator("setFilter", "status", "in", ["post-event"]);
}
})})
It seems to be necessary to set the data individually for each table. I would like to specify the data in the constructor, but this doesn't seem to work. I will continue to try to refine this.
If you are getting the data from a URL you can pass it into the ajaxURL property in the table constructor and tabulator will make the request for you:
$("#example-table").tabulator({
ajaxURL:"/cgi-bin/getevents.pyw", //ajax URL
columns:[...] //define columns etc
});
I have an ajax country/city selector. When I select United States the browser crashes. Doh!
I have a dropdown list of countries. When I select a country a jQuery ajax call is run which gets a JSON response of cities belonging to that country.
I should have seen it coming when I had to increase my allowed memory during execution. Here's the JSON response from selecting the UK.
{"5947":"Aberdeen","12838":"Aberystwyth","15707":"Aldershot","18575":"Alsagers Bank","18682":"Altrincham","4863":"Andover","41802":"AOL","6471":"Armagh","18945":"Ascot","4864":"Ashby-de-la-Zouch","4865":"Ashford","5948":"Aviemore","12985":"Aylesbury","12281":"Ballymena","14446":"Banbury","12445":"Bangor","13631":"Barking","4866":"Barnet","17004":"Barnsley","16423":"Barrow-in-Furness","16254":"Basildon","12402":"Basingstoke","5826":"Bath","13289":"Beddgelert","15082":"Bedford","4868":"Belfast","4869":"Belper","13874":"Benfleet","5827":"Benson","15514":"Berkhamsted","4870":"Berwick Upon Tweed","12948":"Betws-y-Coed","18776":"Bexley","14530":"Bicester","4871":"Billericay","18436":"Birkenhead","4872":"Birmingham","14592":"Blackburn","14686":"Blackpool","12526":"Bolton","12480":"Bournemouth","13062":"Bracknell","18772":"Bradford","4873":"Braemar","4874":"Brecon","4875":"Brentwood","18820":"Brighton","14260":"Bristol","4876":"Broomfield","42004":"Burgess Hill","14654":"Burnley","4877":"Burton Upon Tren","13812":"Bury","15835":"Bury St Edmunds","16500":"Camberley","4878":"Cambridge","4879":"Canterbury","5957":"Cardiff","14443":"Carlisle","14065":"Carrickfergus","42384":"Chalgrove","5832":"Chatham","13641":"Chelmsford","4880":"Cheltenham","4881":"Chester","42879":"Chesterfield","12160":"Chichester","41768":"Chorley","14056":"Church Stretton","5949":"Cladich","4884":"Colchester","16204":"Congleton","17534":"Coniston","42888":"Corsham","4885":"Coventry","13575":"Crawley","15410":"Crewe","13913":"Croydon","4886":"Cumbernauld","13711":"Dartford","4887":"Dartmouth","5833":"Derby","17468":"Derry","4889":"Doncaster","13696":"Dorchester","15377":"Dorking","5834":"Dover","16659":"Dudley","41867":"Dumbarton","18091":"Dumfries","4890":"Dunbar","14217":"Dunblane","4891":"Dundee","14067":"Dunfermline","4892":"Durham","16058":"East Molesey","17521":"East Preston","12501":"Eastbourne","12374":"Eastrea","4893":"Edinburgh","18992":"Elgin","41763":"Ellesmere","12883":"Ely","16825":"Enfield","14510":"Epsom","5835":"Exeter","4894":"Falkirk","5836":"Falmouth","42388":"Faringdon","42034":"Farmington","14604":"Farnham","42347":"Feltham","12829":"Fleet","4895":"Forres","42315":"Frosterley","5950":"Glasgow","4896":"Glastonbury","12562":"Gloucester","15956":"Gosport","4898":"Grangemouth","12626":"Gravesend","16057":"Grays","4899":"Great Wilbraham","4900":"Greenock","12752":"Grimsby","11747":"Guildford","14506":"Guilford","11938":"Halifax","5010":"Hamilton","15553":"Harlow","41733":"Harpenden","14713":"Harrow","4902":"Hartlepool","18952":"Haslemere","13977":"Hastings","14917":"Hatfield","12529":"Haverfordwest","4903":"Haverhill","4904":"Hawarden","5951":"Hawick","11776":"Hemel Hempstead","15302":"Hereford","14999":"Hertford","41893":"Heston","16056":"Hexham","13019":"High Wycombe","13643":"Hoddesdon","5958":"Holyhead","12420":"Hornchurch","14160":"Horsham","12108":"Huddersfield","5837":"Hull End","13296":"Huntingdon","14801":"Hyde","17707":"Ilford","41721":"Inverkeithing","4905":"Inverness","5838":"Ipswich","4906":"Keighley","4907":"Kelso","18628":"Kendal","17805":"Kenilworth","4908":"Kennet","4909":"Kettering","18578":"Kidsgrove","18984":"Kilmarnock","4910":"Kingston Upon Hull","5952":"Kirkwall","18257":"Lakenheath","15425":"Lampeter","13182":"Lancaster","4911":"Laughton","13488":"Leamington","18824":"Leeds","13135":"Leek","17849":"Leicester","17716":"Leigh","12836":"Lerwick","13387":"Letchworth","4912":"Lewes","41767":"Leyland","13546":"Lichfield","5840":"Lincoln","19039":"Little Chalfont","16778":"Liverpool","13442":"Llandrindod Well","5953":"Loch Ness","12008":"London","15035":"Loughborough","15518":"Loughgall","15011":"Louth","18492":"Lowestoft","14023":"Luton","4913":"Machynlleth","12416":"Maidenhead","12230":"Maidstone","14722":"Manchester","4914":"Mansfield","4915":"Margate","4916":"Marlborough","17889":"Marlow","18870":"Melborne","16170":"Melton Mowbray","4917":"Merton","5844":"Middlesbrough","5959":"Milford","15181":"Millom","12315":"Milton Keynes","12089":"Mold","18816":"Montrose","5954":"Motherwell","18574":"Nantwich","4918":"Newark","17097":"Newbury","5845":"Newcastle","4919":"Newcastle Upon Tyne","19040":"Newport","41682":"Newquay","13629":"Northallerton","4922":"Northampton","18577":"Northwich","42209":"northwold","15080":"Norwich","5847":"Nottingham","4923":"Oban","11975":"Oldham","6474":"Omagh","17161":"Oxford","15422":"Oxshott","18627":"Penrith","4925":"Penzance","16404":"Perth","5848":"Peterborough","4926":"Plains","4927":"Plymouth","15551":"Pontypridd","14208":"Poole","4928":"Portsmouth","17642":"Portstewart","41766":"Preston","5011":"Prestwick","18579":"Radway Green","42069":"Ramsgate","11775":"Reading","14706":"Redditch","16276":"Ringwood","15522":"Ripon","14673":"Rochester","15968":"Romford","41857":"Rugby","15289":"Runcorn","17520":"Rustington","14052":"Saint Albans","16462":"Salford","4931":"Salisbury","42295":"Sandwich","17690":"Sandy","4932":"Scarborough","13975":"Seaford","12003":"Shaftesbury","18891":"Sheffield","5850":"Shrewsbury","13178":"Slough","14708":"Solihull","4935":"Southampton","4936":"Southborough","14524":"Southend-on-Sea","13970":"Southport","42260":"St Albans","5955":"St Andrews","15841":"St Asaph","18576":"St Helens","16114":"St Ives","12717":"Stafford","41746":"Staines","14051":"Stanmore","16656":"Stansted","42032":"Stevenage","5012":"Stirling","11801":"Stockport","14198":"Stockton-on-Tees","4937":"Stoke On Trent","42386":"Stranraer","4938":"Stratford-Upon-Avon","4939":"Stroud","18615":"Sudbury","11860":"Sunderland","16393":"Sutton","5960":"Swansea","12853":"Swindon","4941":"Taunton","5851":"Teeside","13973":"Telford","4943":"Truro","17702":"Virginia Water","5852":"Waddington","12059":"Wakefield","4945":"Wallingford","4947":"Wareham","5853":"Warrington","4948":"Warwick","4949":"Watford","12009":"Wellingborough","12528":"Wellington","13366":"Wells","12530":"Welwyn Garden City","16785":"Weston Under Lizard","16334":"Wetherby","18171":"Weymouth","4950":"Whitby","13308":"Whitehaven","42387":"Whitehead","5956":"Wick","17581":"Wilmslow","5854":"Wimbledon","12524":"Wimborne Minster","12551":"Winchester","15946":"Windsor","18573":"Winsford","4952":"Wisbech","4953":"Wisborough Green","12982":"Woking","18769":"Wokingham","13287":"Wolverhampton","17904":"Woodford","18086":"Woolavington","11783":"Worcester","12128":"Worthing","5961":"Wrexham","13630":"Yarm","17015":"Yeovil","11824":"York"}
Here is my Javascipt:
$('#current-country').change(function(){ //any select change on the dropdown with id country trigger this code
$('.select-current-city').show();
$("#current-cities > option").remove(); //first of all clear select items
var country_id = $('#current-country').val(); // here we are taking country id of the selected one.
$.ajax({
type: "POST",
url: "<?php echo base_url()?>map/get_cities/"+country_id, //here we are calling our user controller and get_cities method with the country_id
success: function(cities) //we're calling the response json array 'cities'
{
$.each(cities,function(id,city) //here we're doing a foeach loop round each city with id as the key and city as the value
{
var opt = $('<option />'); // here we're creating a new select option with for each city
opt.val(id);
opt.text(city);
$('#current-cities').append(opt); //here we will append these new select options to a dropdown with the id 'cities'
});
}
});
});
Has anyone any suggestions on how I can process this much data in the browser?
I'm using PHP (Codeigniter), MySQL and jQuery.
I would recommend creating an array of your new option nodes, and then appending them en masse. Doing them one at a time may be what's killing you.
var newOptions = [];
$.each(cities,function(id,city) {
var opt = $('<option />', { "val": id, "text": city });
newOptions.push(opt[0]); //need to push actual dom node - thanks RightSaidFred
});
$('#current-cities').append(newOptions);
Or should this be clearing previous options in the dropdown? If so:
$('#current-cities').html(newOptions);
First double check you are not making the same AJAX request more than once.
A quick and easy solution would be to split your AJAX requests in more than one. Start by dividing it into two, and if that still isn't enough, divide them by 3 or more.
You can then check which JSON size is the right one for you, and use that one. Or you can have your php determine how much requests are needed based on the number of total cities.
For really large lists, I had to split the requests into 100 items each. The first request I would get the first piece of list along with a bit of data indicating how much requests I had to make to obtain the full list, append the new nodes, then I would make the remaining requests, until I got the full list.
I don't see how that would crash for you. Here I am doing the worst possible thing and it is blazing fast:
http://jsfiddle.net/HZnYQ/
Each time you select something, I remove all elements one by one and then append them one by one, and it's still instant. Actually my CPU doesn't even make a note of it.
I wanted to develop a Django app and one of the functionalities I'd like to have is dynamic drop-down lists...specifically for vehicle makes and models...selecting a specific make will update the models list with only the models that fall under that make....I know this is possible in javascript or jQuery (this would be my best choice if anyone has an answer) but I don't know how to go about it.
Also, I'd want the make, model, year and series to be common then the other attributes like color, transmission etc to be variables so that one needs only enter the make, model, year, and series only for a new vehicle. Any ideas would be highly appreciated.
The 3 things you mention being common, make, model, year, would be the 3 input values. When given to the server, an object containing the details would be returned to the calling page. That page would parse the object details (using JavaScript), and update the UI to display them to the user.
From the Django side, there needs to be the facilities to take the 3 inputs, and return the output. From the client-side, there needs to be the facilities to pass the 3 inputs to the server, and then appropriately parse the server's response.
There is a REST api framework for Django that makes it rather easy to add the "api" mentioned above -- Piston. Using Piston, you'd simply need to make a URL for that resource, and then add a handler to process it. (you'll still need to skim the Piston documentation, but this should give you an idea of what it looks like)
urls.py:
vehicle_details = Resource(handler=VehicleDetails)
url(r'^vehicle/(?<make>.*)/(?<model>.*)/(?<year\d{2,4}/(?P<emitter_format>[a-z]{1,4}), vehicle_details, name='vehicle_details'),
handler.py:
class VehicleDetails(BaseHandler):
methods_allowed = ('GET',)
model = Vehicles #whatever your Django vehicle model is
def read(self, request, *args, **kwargs):
# code to query the DB and select the options
# self.model.objects.filter()...
# Build a custom object or something to return
return custom_object
This simply sets up the url www.yoursite.com/vehicle/[make]/[model]/[year]/json to return a custom data object in JSON for jquery to parse.
On the client side, you could use jquery to setup an event (bind) so that when all 3 drop downs have a value selected, it will execute a $.get() to the api URL. When it gets this result back, it passes it into the Jquery JSON parser, and gives the custom object, as a javascript object. That object could then be used to populate more drop down menus.
(Big warning, I just wrote the following off the top of my head, so it's not meant to be copy and pasted. It's just for the general idea.)
<script type="text/javascript">
// On document load
$(function() {
$('#dropdown_make').bind('change', checkForValues());
$('#dropdown_model').bind('change', checkForValues());
$('#dropdown_year').bind('change', checkForValues());
});
function checkForValues() {
if ($('#dropdown_make').val() && $('#dropdown_model').val() && $('#dropdown_year').val())
updateOptions();
}
function updateOptions() {
url = '/vehicle/';
url += $('#dropdown_make').val() + '/';
url += $('#dropdown_model').val() + '/';
url += $('#dropdown_year').val() + '/';
url += 'json/';
$.get(url, function(){
// Custom data object will be returned here
})
}
</script>
This is uncanny: Dynamic Filtered Drop-Down Choice Fields With Django
His question:
"Here is the situation: I have a database with car makes and models. When a user selects a make, I want to update the models drop-down with only the models associated with that make. ... Therefore I want to use Ajax to populate the data."
You're not the same guy? :)