How to save results from dynamic cascading drop down list? - javascript
I have 2 cascading drop down list , when page loads, data is loaded to the 1st DDL, when item is selected from this list its goes to the database and fetch matching items and populate 2nd DDL. I want to prevent going twice to the DB on selection.
For example, I am loading 1st DDL with cars manufactures then click on Toyota what happens next is it goes to DB and fetches all Toyota's models and populates the 2nd DDL, after that i select different car manufacture, same thing happens. Now when i select again Toyota from the 1st list it will not go to DB, it will pull the data from previous request.
I will like to keep an object (like dictionary) of the requests, so if item is already been requested it will not go back to the DB but use local saved data.
You can store list return from server through LocalStorage in Javascript. By using setItem()function as shown below.
window.localStorage.setItem('Toyota', 'Models');
Where Toyota is the key and Models is the value. Also note that LocalStorage can only store strings.
To store arrays or objects you would have to convert them to strings.
To do this we use the JSON.stringify() method before passing to setItem() .
const models = {
1: "Model-1",
2: "Model-2",
}
window.localStorage.setItem('Toyota', JSON.stringify(models));
Now when ever you select different car manufacture check its value in LocalStorage object first.
It accepts only one parameter which is the key and returns the value as a string.
To retrieve the Toyota key stored above:
window.localStorage.getItem('Toyota');
This returns a string with value as.
“{“1”:”Model-1”,”2”:”Model-2”}”
To use this value, you would have convert it back to an object.
To do this, we make use of JSON.parse() method which converts a JSON string into a Javascript Object.
JSON.parse(window.localStorage.getItem('Toyota'));
Please keep in mind to check weather your browser support Local storage or not.
if (typeof(Storage) !== "undefined") {
// Code for localStorage
} else {
// No web storage Support.
}
You can use Map() object to store the data based on key.
Find more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
var brandModelsList = new Map();
function getBrandModels(brand)
{
if(brandModelsList.has(brand))
{
modelList = JSON.parse(brandModelsList.get(brand));
// Do stuff with second dropdown
}
//Your ajax stuff to get data
$.ajax({
url: 'server url',
data: {name: brand},
dataType: 'json',
}).done(response => {
brandModelsList.set(brand, JSON.stringify(response));
// Do stuff with second dropdown
});
}
It has support in most of modern browsers. A best tutorial on this is https://javascript.info/map-set-weakmap-weakset.
you can do this via JavaScript as #Rahat Hameed stated if the cascading dropdown values are not changed frequently, if they can be changed e.g (you can updated them via some logic).Then preferable ways it to fetch them from database.
Related
Select2 with remote data and pre-selecting custom options
I'm using Select2 4.0.3 with remote data and server paging and it is working fine. The data returned from the api has additional fields, something like this: var data = { id: 12345, text: 'John Doe', email: 'abc#123.com' } According to the documentation here is the example of how to pre-select an option when using remote data, which also works fine (with one issue as noted below): $.ajax({ type: 'GET', url: '/api/students/s/' + studentId }).then(function (data) { // create the option and append to Select2 var option = new Option(data.full_name, data.id, true, true); studentSelect.append(option).trigger('change'); // manually trigger the `select2:select` event studentSelect.trigger({ type: 'select2:select', params: { data: data } }); }); The documentation explains about the last bit: Notice that we manually trigger the select2:select event and pass along the entire data object. This allows other handlers to access additional properties of the selected item. In my case the additional property is the email field. My question/problem is that for the above 'manually' appended option the select2 JSON data has only the id and text fields. If the user clicks the list and allows it to populate the data from the server then the data has the id, text, and email fields as it should. The documentation statement explaining why the trigger was needed indicates to me that the data object returned from the api would be passed on (and presumably used), but this does not seem to be the case. So in my case leaving out or including the trigger portion has no effect of the control, so am I misunderstanding the purpose of that code or is select2 not working as documented? For Example, in the templateSelection function used to format the control selection, which is called when the append and trigger code is executed, the object passed to the function has only the id and text properties. In fact if the original data object had completely different properties, say name, value, and email, the data object passed to the function still has id and text only, which is the default object used by select2. This behaviour forced me to modify the object to conform to the {id, text} pattern.
Angular $http.query use array as params?
I have an Angular 1 application where a user can select several items by clicking on a checkbox. When he clicks a particular button, these Ids are aggregated as this: angular.forEach($scope.orders, function (order, id) { if (order.export) { ids.push(order.id); } }); Now I want to query these Ids in my php api action, but how can I transmit all id's to my apiAction? I tried it with $http.query ($scope.items = Items.query({ 'ids' : ids});), but this does not work as it creates a strange url which can not be parsed. Then I tried it with: $http.save and there I can transfer the ids to my api action, but I also need to return the result back to my application in order to show it on the frontend, but with using $http.save this does not seem to be possible, as I always get a orderBy:notarray error. So how can I use $http.save and get a proper array in return or use $http.query with an array with multiple ids in the params? Thanks
In angular $http call... params: { id: JSON.stringify(ids) }
You can convert your array into comma separated string and then pass that string into your php api and then you can get back your array in php. E.g., Angular side, ids.toString() PHP side, explode(",",$query_param);
Dynamic Dropdown from Mongo
I have a mongoDB with a DB = Staff and a collection = records. It contains a list of employees, and one of the fields is workgroup and it has items like "management", "union", "support staff", etc. I want to populate a dropdown list with the values of the workgroup field and then use it to retrieve all records from the specified value selected in the dropdown. I am using Ruby and I can retrieve the values ok (I can see them in the console), but they do not populate the dropdown list. This is my Ruby statement: get '/workgroup' do Record.all.to_a.collect(&:workgroup).uniq.to_json end My attempt at the javascript is: <script> //var json = 'http://localhost:4567/api/v1/workgroup'; $(document).ready(function() { $.getJSON("/api/v1/workgroup",function(obj) { $.each(json.records,function(key,value) { var option = $('<option />').val(value.workgroup); $("#dropDownDest").append(option); }) }) }); </script> Once I get the information in the dropdown, I want to use it to return all records with that workgroup value to a table. I haven't figured that part out yet. One step at a time! Thanks!!
Within your $.getJSON call back you are referencing json.records, but there is no json variable or object. The argument you are passing to the callback is obj. I think you really want to be doing $.each(obj, function(key, value)...
How do handle empty arrays when serializing only altered form elements
I have a form that consists of a number of multi-select fields. Each select has a unique ID, and are named accordingly: values[foobar1][] values[foobar2][] values[foobar3][] ... etc. This form could potentially contain hundreds of these fields, and so is paged by ajax. The result of that is that there is no guarantee that all records are going to available at once at the front end. Therefore, it is impossible for me to submit the entire form. I do, however, have access to the entire list of records server-side. My solution to this was to watch for changes in the form fields and, for every field that is changed, store the values in an array to keep track of just the altered field values. So if you make a change to just foobar2, the resulting serialized array that is sent to the server will look like this: 0: Object { name: "values[foobar2][]" value: "thevalue1" }, 1: Object { name: "values[foobar2][]" value: "thevalue3" } So this works fine except for, as you may have guessed, when the select multiple is emptied. No matter what format I use for storing the altered values, be it arraySerialization of each field or as an associative array, when I pass my array to $.param() for the ajax request the resulting serialized string contains no trace of the empty value. So there is no way for the server to determine that the value has been emptied. Can anyone suggest a way of either passing the data to the server so that the empt(ied) array remains intact, or another way of dealing with the initial problem. Thanks in advance!
You want to calculate the diff between current and previous state, send the change to the server, and apply it to the data. You can do so using the JSON patch standard (rfc6902). JSON Patch is a format for describing changes to a JSON document. It can be used to avoid sending a whole document when only a part has changed. When used in combination with the HTTP PATCH method it allows partial updates for HTTP APIs in a standards compliant way. To create the diff you can use an NPM module, such as jiff. A diff is set a of patching commands, that can transform a JSON document. For example: [ { "op": "replace", "path": "/values/foobar2/", "value": ["thevalue1"] }, { "op": "remove", "path": "/values/foobar2/"} ] You send the diff to the server, and then use a server module, such as php-jsonpatch, to apply the patch to the current data on the server.
Create a single object for all select field values you can use localStorage or sessionStorage to store it. Since the form is in a lot of pages and you use ajax to get each select field. Place the selected values of each field in an array. Creating an object like this is the idea. { formValues: { foobar1: { values: ["thevalue1","thevalue2"] }, foobar2: { values: ["thevalue3"] }, ... foobarN: { values: [] } } } Every time you update a select vield value or values make sure to update the localStorage saved value. e.g. var valuesObject = { formValues: { foobar1: { values: ["thevalue1","thevalue2"] }, foobar2: { values: ["thevalue3"] }, foobar3: { values: [] } } } // Put the object into storage localStorage.setItem('valuesObject', JSON.stringify(valuesObject)); // Retrieve the object from storage var valuesObjectA = localStorage.getItem('valuesObject'); //console.log('valuesObject: ', JSON.parse(valuesObjectA)); // post your data $.post( "ajax.php", valuesObjectA ).done(function( data ) { alert( "Data Loaded: " + data ); }).fail(function() { console.log( "error" ); }); Sample fiddle
Too much JSON crashes browser :(
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.