I'm trying to implement an input field with an autocomplete feature. I'd be using Google Books API to autocomplete names of books based on the keyword that the user enters in the input text field. I'd be using Django as my framework to implement this feature.
This is what I have been able to do so far:
JS
$( document ).ready(function()
{
$("#id_book_name").on("change paste keyup", function()
{
var app_url = document.location.origin;
var book_name = $('#id_book_name').val();
var url = app_url+'/book-search/';
if(book_name.length > 4)
{
var data = {
'book_name': book_name,
'csrfmiddlewaretoken': document.getElementsByName('csrfmiddlewaretoken')[0].value,
};
console.log(data);
$.post(url, data).done(function(result)
{
for(var book_title of result)
{
console.log(book_title);
}
console.log(result);
}).fail(function(error)
{
console.log(error)
});
return false;
}
});
});
Here, #id_book_name is the id of my input text field. As soon as the length of the keyword entered by the user exceeds 4, I'm sending a POST request to /book-search which is mapped to the following Python function where I hit Google Books API's endpoint and return the book titles in a specific JSON format:
def book_search(request):
book_results = {'titles':[]}
key = 'XXXXXXX'
url = 'https://www.googleapis.com/books/v1/volumes?q=' + request.POST['book_name'] + '&maxResults=5&key=' + key
result = requests.get(url)
json_result = json.loads(result.text)
if 'items' in json_result:
for e in json_result['items']:
if 'industryIdentifiers' in e['volumeInfo']:
isbn = ''
for identifier in e['volumeInfo']['industryIdentifiers']:
isbn = identifier['identifier'] if (identifier['type'] == 'ISBN_10') else isbn
if 'subtitle' in e['volumeInfo']:
book_results['titles'].append(e['volumeInfo']['title'] + ' - '
+ e['volumeInfo']['subtitle'] + ' (' + isbn + ')')
else:
book_results['titles'].append(e['volumeInfo']['title'] + ' (' + isbn + ')')
result = json.dumps(book_results)
return HttpResponse(result)
Sample return format of the above function for keyword 'python':
{"titles": ["Python - A Study of Delphic Myth and Its Origins (0520040910)", "Python Machine Learning (1783555149)", "Learn Python the Hard Way - A Very Simple Introduction to the Terrifyingly Beautiful World of Computers and Code (0133124347)", "Natural Language Processing with Python - Analyzing Text with the Natural Language Toolkit (0596555717)", "Python (0201748843)"]}
Now, what I'm not able to figure out is how to loop through the above JSON format to display the results below my input text field. I know I can use the append() JQuery function to add my book titles inside the <li> tags. However, I'm stuck on how to loop through my response result to individually get each book title using a for loop:
for(var book_title of result)
{
console.log(book_title);
}
I'm new to JQuery, and would really appreciate some guidance on this one. Thanks!
Your requirement is quite simple. One way to achieve this is ..please follow the comments
$(function() {
var myDiv = $("#mydiv"); //Assuming there is a div wrapped
var myUl = $('<ul/>'); //blank unordered list object
//Your result from the query
var result = JSON.parse('{"titles": ["Python - A Study of Delphic Myth and Its Origins (0520040910)", "Python Machine Learning (1783555149)", "Learn Python the Hard Way - A Very Simple Introduction to the Terrifyingly Beautiful World of Computers and Code (0133124347)", "Natural Language Processing with Python - Analyzing Text with the Natural Language Toolkit (0596555717)", "Python (0201748843)"]}');
//Iterate through each object
result.titles.forEach(function(item) {
var li = $('<li/>'); //create an li item object
li.append(item); // append the item/txt to the list item
myUl.append(li); //append the list item to the list
});
myDiv.append(myUl) //Append list to the div
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='mydiv'>
<input id='id_book_name' />
</div>
Let us know
Firstly, there is no reason to return a one-key dictionary. Just return the array. So, your result would look more like:
["Python - A Study of Delphic Myth and Its Origins (0520040910)", "Python Machine Learning (1783555149)", "Learn Python the Hard Way - A Very Simple Introduction to the Terrifyingly Beautiful World of Computers and Code (0133124347)", "Natural Language Processing with Python - Analyzing Text with the Natural Language Toolkit (0596555717)", "Python (0201748843)"]
Then, you pass a fourth parameter to $.post, the datatype of JSON, so it always automatically parses it into a JavaScript array.
$.post(url, data, onSuccess, 'json').fail(onFail);
Then you just have a simple array to append to search results.
Make an array of, say, 5 suggestions, and only fill the top 5 (As more would probably be uneccessary). Then use CSS to hide empty ones (Like #auto-complete :empty { display: none; }). Your onSuccess function could look like (Assuming you have a ol or ul element with the id auto-complete that has 5 li elements):
var autoCompleteBoxes = $('#auto-complete li');
$.post(url, data, function(data) {
for (var i = 0; i < 5; i++) {
autoCompleteBoxes[i].text(data[i] || '');
}
}, 'json').fail(function() {
// Reset auto complete boxes if there was a failure.
for (var i = 0; i < 5; i++) {
autoCompleteBoxes[i].text('');
}
$('#auto-complete').hide();
}
Related
I am new to APPS for OFFICE
I am trying a simple code in which I Validate Excel Data.
So Rather than nesting things in ctx.sync() again and again, I am writing code like this:-
// **json** object used beneath is somewhat like:
{"Field":[
{"FieldName":"Field1", "FieldDesc":"Field 1 desc", "MappedTo":"B2", "IsMandatory":"true", "LOV":"1,2,3"}]}
// **LOV** in above json data means:- the field data can only be among the values given.
//********** MY PIECE OF CODE**************
var fieldData = "";
$.each(json, function (index, field) {
range = ctx.workbook.worksheets.getActiveWorksheet().getRange(field.MappedTo + ":" + field.MappedTo);
range.load('text');
ctx.sync();
fieldData = range.text;
if(field.IsMandatory == true && (fieldData == "" || fieldData == null))
{
headerValidation = headerValidation + "Data is required for Field : " + field.FieldDesc + "\n";
}
else if(field.LOV != "" )
{
if($.inArray(fieldData, field.LOV.split(',')) == -1)
{
headerValidation = headerValidation + "Data not among LOV for Field : " + field.FieldDesc + "\n";
}
}
range = null;
});
As can be seen, I need to read range object again and again. So I am using range object everytime with different address and calling first "load()" and then "ctx.sync()".
If i debug slowly , things do work fine but on running application i get frequent error now and then:-
The property 'text' is not available. Before reading the property's
value, call the load method on the containing object and call
"context.sync()" on the associated request context.
Please guide me how I can handle this?
Also , is my approach correct?
In terms of what's wrong, the error is next to your ctx.sync() statement.
You need it to be
ctx.sync()
.then(function() {
fieldData = range.text;
...
});
And I wouldn't forget the .catch(function(error) { ... }) at the end, either.
As far as reusing the variable or not, it really doesn't matter. By doing "range = ctx.workbook...." you're actually creating a global range variable, which is considered bad practice. Better to do "var range = ctx.workbook....". And you don't need to worry about setting it to null at the end.
One thing to note is that since you're doing this in a for-each loop, note that the number of concurrent .sync()s that can be happening is limited (somewhere around 50-60, I believe). So you may need to adjust your algorithm if you're going to have a large number of fields.
Finally, you can make this code much more efficient by simulataniously ceating all of your range objects, loading them all at once, and then doing a single ".sync".
var ranges = [];
$.each(json, function (index, field) {
var range = ctx.workbook.worksheets.getActiveWorksheet().getRange(field.MappedTo + ":" + field.MappedTo);
range.load('text');
ranges.push(range);
});
ctx.sync()
.then(function() {
// iterate through the read ranges and do something
})
Hope this helps,
~ Michael Zlatkovsky, developer on Office Extensibility team, MSFT
I use this site to download required info about lat/ lon of districts of India.
http://india.csis.u-tokyo.ac.jp/
Can I get all the districts of a give state using python? For e.g. if I choose State: maharashtra I get the list of districts like Akola, Raigarh in the next drop down list. I need that info as a python list.
I can see that a javascript function is called and it is getting the data from /api/ directory.
function setDistrictList() {
var selected = "";
state = $("#state_list").val();
districts = {};
url = "/api/getDistrictList";
Is it possible that I can get this list of districts programmatically using python?
update:
I have tried this function. But that returns the results and not the Java Script drop down list that I expect.
def __getVillageMarkersFromWeb(self,query, state_code=None, district_code=None):
stateString = '"state":"' + state_code + '"' if state_code is not None else ""
districtString = ',"district":"' + district_code + '"' if district_code is not None else ""
f_param = '{' + stateString + districtString + '}'
params = urllib.urlencode({"q":query,"f":f_param})
url = "http://india.csis.u-tokyo.ac.jp/geocode-cgi/census_ajax_json.cgi"
http = httplib2.Http()
response, markers_xml = http.request(url, "POST", params)
dom = minidom.parseString(markers_xml)
markers = dom.getElementsByTagName("marker")
return markers
You could, using BeautifulSoup.
BeautifulSoup allows you to target elements with particular class/id after you've gotten the markup from a page using Requests/urllib/urllib2.
Then you can loop through your BS object and save each to your list.
If the content on a page is generated with JavaScript, PhantomJS can emulate the JS before the markup is scraped.
When I design interfaces, I can't be bothered to create believable dummy text. At the same time, however, I don't want to just copy and paste the same exact data over and over, because then the interface doesn't look terribly realistic.
I have generated a Master JSON Schema that contains the most common types of data I use. I'd like to be able to do something like this when I'm writing HTML:
<ul>
<li>{first_name}</li>
<li>{first_name}</li>
...
<li>{first_name}</li>
</ul>
OR
<ul>
<li data="{first_name}"></li>
<li data="{first_name}"></li>
...
<li data="{first_name}"></li>
</ul>
...whereby every instance of {first_name} is replaced with a random first name from my JSON file. Likewise for any other variable I have in there ( last_name, email, address, country, sentence, etc... )
Is there a way to do this without PHP in something like jQuery? I imagine it'd have to be something like:
foreach(var) {
return randomData(var);
}
Ideally I'd have a very simple, generalized function that would comb through the UI looking for any and all tags and replace each one with a random piece of data from the master schema JSON file.
Click below to see solution I came up with thanks to Billy's help:http://jsfiddle.net/JonMoore/5Lcfz838/2
between http://chancejs.com/ and http://handlebarsjs.com/ you can generate lots of repeatable, seeded random data structures...
// get references to DOM elements
var tplElm = document.getElementById('template');
var tgtElm = document.getElementById('target');
// compile handlebars template
var template = Handlebars.compile(tplElm.innerText);
// initialise chance with seed
// change the seed to change the output data
var chance = new Chance(1234567890);
// create array of random data
var people = [];
for(var i=0;i<10;i++){
people.push({
first_name: chance.name()
});
}
// apply data to template, and insert into page
tgtElm.innerHTML = template({
people: people
});
<!-- load dependencies -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chance/0.5.6/chance.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.3/handlebars.min.js"></script>
<!-- define template -->
<script id="template" type="text/template">
<ul>
{{#people}}
<li>{{first_name}}</li>
{{/people}}
</ul>
</script>
<!-- placeholder for output -->
<div id="target"></div>
Something like this will give you what you want:
var json = [{ "first_name": "bob"}, {"first_name": "sam"}, {"first_name": "bill"}];
var randomnum = Math.floor((Math.random() * 3));
console.log(json[randomnum].first_name);
You can access this data using AJAX and then get elements using Math.random.
Then, with the help of jQuery you can dynamically generate li items.
Let's suppose you have a div element like <div id="anyId"></div> where you will put your ul and lis.
function getElementsFromMasterSchema(count, callback) {
var items = [];
var usedIds = {};
$("h3").text("Request sent...Loading...");
timer = setInterval(function() {
$("h3").text("Loading..." + (time++) + " seconds passed.");
}, 1000);
$.ajax({
url: "https://www.mockaroo.com/37dcc3b0/download?count=100&key=37b5a7c0",
method: "GET"
}).done(function(dt) {
var length = dt.length;
while (items.length < count) {
var randomItem = dt[Math.round(Math.random() * length)];
if (usedIds[randomItem.id] !== true) {
usedIds[randomItem.id] = true;
items.push(randomItem);
}
}
callback(items);
});
}
getElementsFromMasterSchema(10, function(result) {
var ul = $("<ul/>");
for (var i = 0; i < result.length; i++) {
$("<li/>").text(result.first_name).appendTo(ul);
}
$("#anyId").append(ul);
});
Note that you need to make requests from the same domain or set Access-Control-Allow-Origin header in order to make cross-domain requests.
However, this method is working slowly. It takes from 5 to 20 seconds to load this file. Loading a large file from a server and using only some of data is a bad approach.
You definitely need to implement this algorithm on a server side. Something like SQL SELECT TOP n FROM x ORDER BY newid() (SELECT * FROM x ORDER BY RAND() LIMIT n).
I'm using Backbone.js/Underscore.js to render a HTML table which filters as you type into a textbox. In this case it's a basic telephone directory.
The content for the table comes from a Collection populated by a JSON file.
A basic example of the JSON file is below:
[{
"Name":"Sales and Services",
"Department":"Small Business",
"Extension":"45446",
},
{
"Name":"Technical Support",
"Department":"Small Business",
"Extension":"18800",
},
{
"Name":"Research and Development",
"Department":"Mid Market",
"Extension":"75752",
}]
I convert the text box value to lower case and then pass it's value along with the Collection to this function, I then assign the returned value to a new Collection and use that to re-render the page.
filterTable = function(collection, filterValue) {
var filteredCollection;
if (filterValue === "") {
return collection.toJSON();
}
return filteredCollection = collection.filter(function(data) {
return _.some(_.values(data.toJSON()), function(value) {
value = (!isNaN(value) ? value.toString() : value.toLowerCase());
return value.indexOf(filterValue) >= 0;
});
});
};
The trouble is that the function is literal. To find the "Sales and Services" department from my example I'd have to type exactly that, or maybe just "Sales" or "Services". I couldn't type "sal serv" and still find it which is what I want to be able to do.
I've already written some javascript that seems pretty reliable at dividing up the text into an array of Words (now updated to code in use).
toWords = function(text) {
text = text.toLowerCase();
text = text.replace(/[^A-Za-z_0-9#.]/g, ' ');
text = text.replace(/[\s]+/g, ' ').replace(/\s\s*$/, '');
text = text.split(new RegExp("\\s+"));
var newsplit = [];
for (var index in text) {
if (text[index]) {
newsplit.push(text[index]);
};
};
text = newsplit;
return text;
};
I want to loop through each word in the "split" array and check to see if each word exists in one of the key/values. As long as all words exist then it would pass the truth iterator and get added to the Collection and rendered in the table.
So in my example if I typed "sal serv" it would find that both of those strings exist within the Name of the first item and it would be returned.
However if I typed "sales business" this would not be returned as although both the values do appear in that item, the same two words do not exist in the Name section.
I'm just not sure how to write this in Backbone/Underscore, or even if this is the best way to do it. I looked at the documentation and wasn't sure what function would be easiest.
I hope this makes sense. I'm a little new to Javascript and I realise I've dived into the deep-end but learning is the fun part ;-)
I can provide more code or maybe a JSFiddle if needed.
Using underscore's any and all make this relatively easy. Here's the gist of it:
var toWords = function(text) {
//Do any fancy cleanup and split to words
//I'm just doing a simple split by spaces.
return text.toLowerCase().split(/\s+/);
};
var partialMatch = function(original, fragment) {
//get the words of each input string
var origWords = toWords(original + ""), //force to string
fragWords = toWords(fragment);
//if all words in the fragment match any of the original words,
//returns true, otherwise false
return _.all(fragWords, function(frag) {
return _.any(origWords, function(orig) {
return orig && orig.indexOf(frag) >= 0;
});
});
};
//here's your original filterTable function slightly simplified
var filterTable = function(collection, filterValue) {
if (filterValue === "") {
return collection.toJSON();
}
return collection.filter(function(data) {
return _.some(_.values(data.toJSON()), function(value) {
return partialMatch(value, filterValue);
});
});
};
Note: This method is computationally pretty inefficient, as it involves first looping over all the items in the collection, then all the fields of each item, then all words in that item value. In addition there are a few nested functions declared inside loops, so the memory footprint is not optimal. If you have a small set of data, that should be OK, but if needed, there's a number of optimizations that can be done. I might come back later and edit this a bit, if I have time.
/code samples not tested
I need help understanding the best way to manipulate a JavaScript data structure (if you call it that?). I come from a Perl background and am trying to iterate through a JavaScript data structure to update a div (or table) on the fly. Currently I'm just making the data a variable (data) to get going. once I have that sorted then I will use jquery's ajax methods to get the data as a json object on the fly. It's my understanding that the data structure I'm currently generating is the same thing I would get if it were json?? if not then I guess thats another question..
I have searched many times for such a tutorial but they are all close but haven't found one that meets my needs...
I would like to in the future be able to re-order the array and repopulate the #data_table div so part of my question is: is this even the best way to represent the data? Then I want to update just one part of the array like a companies phone number or add a person then redraw the div from this updated array...
The end result to the user could look like:
Company: 99 Beans, Auckland, Ph: 360499
People:
Matt, 6471
Christiaan, 6472
Michelle, 6473
Judy, 6474
Company: ....
* Next company and so forth..
My code so far is like this (except I have 500+ entries in the data array and only 2 here):
I have taken some of this code from another question I found here and tried to make it work for my type of structure..
<script>
$(document).ready(function() {
var r = new Array();
var data= [
{
"id":"6477",
"c":"99 Beans",
"ci":"Auckland",
"p":"09 360499",
"co":[
{"id":"6471","c":" NZ", "n":"Matt" },
{"id":"6472","c":" NZ", "n":"Chrstiaan" },
{"id":"6473","c":" NZ", "n":"Michelle" },
{"id":"6474","c":" NZ", "n":"Judy " },
{"id":"6475","c":" NZ", "n":"Kate " },
{"id":"6476","c":" NZ", "n":"Unknown Person" }
]
},
{"id":"7145", "c":"A1 Ltd", "ci":"Te Puke ","p":"06 870090",
"co":[{"id":"7145","c":" NZ", "n":"Alan" }
]
},
];
// this alert used to work, when I had the data as an array of arrays so there must be a problem with how I'm referencing it or my data structure is wrong??
alert (data.length + " companies" );//+ data[455].co[0].n);
var j = -1;
for (var key=0, size=data.length; key<size; key++) {
// first div
r[++j] ='<div>';
r[++j] = data[key].id;
r[++j] = ' - ';
r[++j] = data[key].c;
r[++j] = ' - ';
r[++j] = data[key].p;
r[++j] = '<br />';
//inner div
var k = -1 ;
for (var key1=0, size1=data[key].d.length; key1<size1; key1++) {
r[++j] ='<div>';
r[++j] = data[key].d.[key1].n + ' - ' + data[key].d.[key1].c + ' - ';
r[++j] = '</div>';
}
r[++j] = '</div>';
}
$('#data_table').html(r.join(''));
});
</script>
<div id="data_table"></div>
JSON's representation in Javascript is a javascript object.
var json = {
my_property: 2,
another_property: 'string!'
}
You can then fetch properties with json.my_property or json['my_property'] (which is nice if you don't know what exactly you are fetching, you can do var something = 'something-the-user-selected'; json[something]).
So, with jQuery, you would so something like this:
var json = {}; // get this from the server using $.AJAX() or $.getJSON()
$("#my_table").append("<tr><td>" + json.my_property + "</tr></td");
Or, if you got an array of, say, tweets, you should iterate over them, maybe with underscore's each, jQuery's each or plain old for (var i = 0; i < json.tweets.length; i++).
The data structure you are referring to is JSON.
With jQuery you can easily iterate your data structure with .each() and treat the returned value as an object with the attributes defined.
Here is a sample making the an Ajax call that will build your JSON object and iterating:
$.ajax({
type: "POST",
url: "MySite/MyResource",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if(data.d.length > 0){
$(data.d).each(function(){$('#mytable').append("<tr><td>Company: " + this.c + "</td></tr>");});
}
},
error: function (xhr, status, error) {
// do something appropriate
}
});
If you are looking to iterate farther then you can continue to call .each()
If data is your JSON value, this will format your specified string. (You'll have to JSONify your string to create it as the actual JSON object)
data.each(function(){
$('#mydiv').append('Company: ' + this.c + ', ' + this.ci + ', Ph: ' + this.p + ' People: ');
this.co.each(function(){$('#mydiv').append(this.n + ', ' + this.id);});
});
For the "rendering an JSON object to HTML", you may want to have a look at jQuery.template() which allows to render pieces of HTML with a template syntax. I have never used it so I do not know how well it can perform with very big lists... (also note that it is still in beta)
UPDATE
Here is a working example (it might not be optimal or anything) but you should see the general idea ;)
http://jsfiddle.net/tsimbalar/A9uYP/
UPDATE 2 for sake of completeness and traceability
There was a syntax error in the original code which prevented the alert() from executing (and all following code).
See comment :
Your code is not working because of a syntax error in
data[key].d.[key1].n., which is invalid syntax. (it should be data[key].d[key1].n, I suppose. Use the Firebug extension ofr Firefox to detect this kind of issues that would otherwise go unnoticed.
After fixing this typo, it is easy to iterate over the contents of the object using jQuery.each() as mentioned by catalpa in another answer. You can then build HTML elements (i.e. $("<div/>")) and append them to existing items of the page with append() . (see the fiddle about that part)
handlebars js templating is far better than jquery template and can be used for rendering complex json objects in html