JQuery and Autocomplete Using a List (Python/Flask) - javascript

This has probably been asked before, but after searching for awhile I'm still a bit confused. I'm trying to make a flask app but I'm not too familiar with JQuery itself, but I would like to create an autocomplete widget in my html. However, instead of querying a database I'd like to just use a static list and a regex to get results. I used this as reference.
What I did was:
#app.route('/autocomplete', methods=['GET'])
def autocomplete():
search = request.args.get('q')
#query = db_session.query(Movie.title).filter(Movie.title.like('%' + str(search) + '%'))
#results = [mv[0] for mv in query.all()]
results = ['Beer', 'Wine', 'Soda', 'Juice', 'Water']
return jsonify(matching_results=results)
while keeping the rest of the code:
<head>
<link href="//code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" rel="Stylesheet"></link>
<script src="//code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="//code.jquery.com/ui/1.10.2/jquery-ui.js" ></script>
<script type="text/javascript">
$(function() {
$("#autocomplete").autocomplete({
source:function(request, response) {
$.getJSON("{{url_for('autocomplete')}}",{
q: request.term, // in flask, "q" will be the argument to look for using request.args
}, function(data) {
response(data.matching_results); // matching_results from jsonify
});
},
minLength: 2,
select: function(event, ui) {
console.log(ui.item.value); // not in your question, but might help later
}
});
})
</script>
</head>
<body>
<div>
<input name="autocomplete" type="text" id="autocomplete" class="form-control input-lg"/>
</div>
</body>
I haven't implemented a regex but I assumed if I typed anything with at least 2 characters I would get a dropdown of the list I wrote above. However, I get nothing. Any ideas on how I can get this autocomplete to work?

Related

How can I add Automatic intellisense to a Textbox using Jquery

I want to add automatic Intellisense (Auto Complete--Filtering Search Result) to a textbox, corresponding to the words that I'm typing in that textbox and the Intellisense is fetched from a database table. How can I achieve this? Can anyone help?
Here is my jQuery code:
$(document).ready(function() {
$('#city').autocomplete({
source:'send.php'
});
});
send.php file given below:
$link=mysqli_connect("localhost","hari","123","hari");
$searchTerm = $_GET['query']; //get search term
$query = $db->query("SELECT fname FROM user WHERE fname LIKE
'%".$searchTerm."%' ORDER BY fname ASC"); //get matched data from user table
while ($row = $query->fetch_assoc()) {
$data[] = $row['fname'];
}
echo json_encode($data);//return json data
Corresponding HTML Code is given below:
<div class="content col-sm-12">
<form>
<h1>Hello!!!</h1>
<input type="text" id="city" name="city" size="20" class="city"
placeholder="Please Enter City or ZIP code"><br><br>
</form>
</div>
You have to include the following scripts in your html page
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
And add the following css in head of your html
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
The mistake you made is the parameter passing with the name term and trying to read with the name query in your php file. In your send.php file change the line
$searchTerm = $_GET['query'];
into
$searchTerm = $_GET['term'];
Try this:
$(document).ready(function() {
$('#city').autocomplete({
source: function( request, response ) {
$.ajax( {
url: "send.php",
dataType: "jsonp",
data: {
query: request.term
},
success: function( data ) {
response( data );
}
} );
},
});
});
I have a recommendation for you, use angular 1 for this, you can simply write that code without additional UI libraries and with much much better performance and issue-free solution.
Add the following parent div to your input element:
Change your input to this:
<input type="text" id="city" name="city" size="20" class="city" ng-model="query" ng-change="fetch()" placeholder="Please Enter City or ZIP code">
Add the following code right under your <input>:
<ul>
<li ng-repeat="text in suggestions">{{ text }}</li>
</ul>
As a basic set up, you need this in your <head> section:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
Finally you will create a new file like in assets directory like "suggestions.js" in your assets directory or somewhere and add this file right before you your </body> tag like this in your template:
<script src="assets/suggestions.js"></script>
The file will have the following lines:
var app = angular.module('myApp', []);
app.controller('suggestionsCtrl', function($scope, $http) {
$scope.suggestions = [];
$scope.query = '';
$scope.fetch = function() {
$http({method: 'POST', url: 'send.php', params: { query: $scope.query } }).
then(function(response) {
$scope.status = response.status;
$scope.suggestions = response.data;
}, function(response) {
/* SOMETHING WENT WRONG WTIH THE CALL DO SOMETHING HERE */
});
};
});
There is very simple set-up/tutorial for Angular 1 here:
https://www.w3schools.com/angular/default.asp
This is not a direct answer but believe me a more efficient answer. Angular 1 and the newer versions save a lot of time and brings performance.
And btw, autocomplete() is not a native jQuery function. Also I do not mention that you need jQuery also for Angular, but I assume it's already added in your template.

Knockout did not render template with my data

Stuck with javascipt's knockout library.
So, I want to implement simple forum. I have javascript file with two ajax requests, for topics and for posts. And I have html template.
function dealModel() {
var self = this;
self.board = ko.observableArray([]);
var res = [];
$.getJSON("http://someaddress/threads", function(data) {
$.each(data, function(i, thread) {
var js = jQuery.parseJSON(thread);
js.posts = ko.observableArray([]);
var postres = []
$.getJSON("http://someadress/posts/" + js.id, function(postdata) {
$.each(postdata, function(idx, post){
var jspost = jQuery.parseJSON(post);
postres.push(jspost);
})
})
js.posts(postres);
res.push(js);
})
self.board(res);
})
}
$(document).ready(function(){
ko.applyBindings(new dealModel());
});
var testdata = [{text : "text 1"} , {text : "text2"}]
This is my js code. It perfectly works with topics, but when I put my posts, my observable array "posts" already empty.
For test I created test array "testdata" (below), and pass in my observable array. And, javascript work perfectly.
Here is my template
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>
<script type="text/javascript" src="ajaxknockout.js"></script>
</head>
<body>
<div class='board'>
<div class='threads' data-bind="foreach: board">
<p data-bind="text: title"></p>
<div class= "posts" data-bind="foreach: $data.posts">
<p data-bind="text: text"> </p>
</div>
</div>
</div>
</body>>
</html>
So, I think something bad with my posts JSON.
Here it is.
["{\"createTime\": \"Monday, 04. January 2016 05:53PM\",\"thread_id\": \"2\",\"text\": \"post 1\",\"id\": \"4\"}", "{\"createTime\": \"Monday, 04. January 2016 05:53PM\",\"thread_id\": \"2\",\"text\": \"post 2\",\"id\": \"5\"}", "{\"createTime\": \"Monday, 04. January 2016 05:53PM\",\"thread_id\": \"2\",\"text\": \"post 3\",\"id\": \"6\"}"]
SO, I have a question. Whats wrong with my code? Why knockout understand my testdata, but completly reject production data?
That's because this part of your first json request:
js.posts(postres);
executes ahead of callback from your second json request where you are pulling posts. You have to change that so the posts array is populated before doing js.posts(postres);, e.g like so:
$.getJSON("http://someadress/posts/" + js.id, function(postdata) {
$.each(postdata, function(idx, post){
var jspost = jQuery.parseJSON(post);
postres.push(jspost);
})
js.posts(postres);
})

Yet another jQuery autocomplete thread

I am trying to figure out what's wrong with my code (Laravel5 project).
So far I have:
master.blade.php
<!-- jQuery Version 1.11.0 and jQuery UI-->
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="{{asset("patho/to/jquery/jquery-1.11.0.js")}}"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
the master view is preloaded in any view, it serves as a header and footer wrapper. Note that I obfuscated the local path to jQuery script, it's real and it's loaded and it works.
Included in one of my CRUD views:
create.blade.php
{!! Form::select('league_id',[], Request::old('league_id'), array('class' => 'form-control', 'id' => 'league_name')) !!}
<script src="{{ asset('js/tips.js') }}">
And in my tips.js file:
$(document).ready(function() {
var dataSource = ['test1', 'test2'];
$('#league_name').autocomplete(
{
source: dataSource
}
);
});
I also tried with:
var dataSource = [{value: 'test1', name: 'test1'}]
Yet it still does not work at all, no console errors displayed either.
Any clues?
Edit: following michaelbahr's suggestion:
the autocomplete widget doesn't execute at all, the resulting select dropdown I get is just an empty one. No items, no search box from the widget. A plain, empty select:
<select class="form-control ui-autocomplete-input" id="league_name" name="league_id" autocomplete="off"></select>
It won't work that way. Autocomplete needs an input (text) field to work. If you wanna have a select, you can do something like this.
But, if you already have an array, you don't need autocomplete, you can simply use jQuery like so:
$(document).ready(function() {
var dataSource = ['test1', 'test2'];
var league = $('#league_name');
$.each(dataSource, function(index, value){
league.append("<option>" + value + "</option>");
})
});

Using initSelection in select2 3.5.2 for custom filtering

I have a text field which uses select2. Here is the initialization:
$("#foo").select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0;
}).length===0)
{return {id:term, text:term};}
},
initSelection : function (element, callback) {
var data = {id: element.val(), text: element.val()};
callback(data);
},
tags:[],
tokenSeparators: [","],
data: [...data goes here...]
});
In this field, the user is supposed to put in a number, or select items from a list. If an item that the user puts in doesn't appear on the list, it ought be created as a simple tag (id and text identical). This works.
What doesn't work is when I set the value programmatically:
myvar.find('.placeholder lorem-ipsum').val(number).trigger("change");
The way it works now, is that when I set it to any value, it takes it without complaint, making a new simple tag. However, if I were to remove the initSelection parameter completely, it would ignore unknown values, and use known values as taken from the list (where tags are complex - id and text are different).
How do I make it so that if the value I set to the field is found on the list, it will use the item, and otherwise make a simple tag? The way it works now (simple tags only) is sort-of acceptable, but I'd prefer it worked ideally.
EDIT:
I've made examples for how it works with and without the initSelection parameter.
http://jppk.byethost12.com/with.html
http://jppk.byethost12.com/without.html
In short, I want it to work like with.html when I push "Add New Item" and I want it to work like without.html when I push "Add Existing Item".
Here is my best interpretation of what you want.
JSFiddle
The trick is to append the element to the select field.
var array = ['foo', 'bar', 'baz']
$.each(array, function(key, value) {
appendSelect(value);
});
$("#foo").select2();
$('#submit').click(function() {
var val = $('#name').val();
$('#name').val('');
array.push(val);
appendSelect(val);
});
function appendSelect(value) {
$('#foo').append($("<option></option>").text(value));
}
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0-rc.1/css/select2.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0-rc.1/js/select2.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<div class='container'>
<select id='foo' multiple="multiple" class='form-control'></select>
<label>Input</label>
<input id='name' />
<button id='submit'>submit</button>
</div>
Much, much later, I found a solution that works right:
initSelection : function (element, callback) {
var eArr = element.val().split(",");
var data = [];
for (var i=0;i<eArr.length;i++) {
for (var j=0;j<preExistingData.length;j++) {
if (preExistingData[j]["id"] === eArr[i]) {
data.push(preExistingData[j]);
continue;
}
}
data.push({id: eArr[i], text: eArr[i]});
}
callback(data);
},
This not only checks if the value is among the pre-existing values (in which case it uses that item), but also if the value is a comma-separated list of values (in which case it properly encapsulates them as JS objects).

Error rendering data with Javascript / KendoUI autocomplete - Object #<Object> has no method 'slice' - how to resolve?

I am following the Using Kendo UI with MVC4 WebAPI OData and EF article. After installing KendoUI and making sure all references are set, I type in three characters, and get the following error:
Uncaught TypeError: Object # has no method 'slice'
Root of the Problem
To save reading through the updates: Through debugging I found that the issue is that JS is expecting to parse an array, where it isn't available in the data - at the root. In the data hierarchy, it's one level in.
Original Problem
I cleaned up kendo.web.min.js and the error is occuring around line 3498:
success: function (n) {
var i = this,
r = i.options;
return i.trigger(wt, {
response: n,
type: "read"
}), n = i.reader.parse(n), i._handleCustomErrors(n) ? (i._dequeueRequest(), t) : (i._pristine = et(n) ? e.extend(!0, {}, n) : n.slice ? n.slice(0) : n, i._total = i.reader.total(n), i._aggregate && r.serverAggregates && (i._aggregateResult = i.reader.aggregates(n)), n = i._readData(n), i._pristineData = n.slice(0), i._data = i._observe(n), i._addRange(i._data), i._process(i._data), i._dequeueRequest(), t)
The Kendo UI widgets are loading just fine as well as the css:
<link href="~/Content/kendo/kendo.common.min.css" rel="stylesheet" />
<link href="~/Content/kendo/kendo.default.min.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/kendo/kendo.web.min.js"></script>
<script src="~/Scripts/kendo/kendo.aspnetmvc.min.js"></script>
<script src="~/Scripts/appScripts.js"></script>
And I am seeing the same error both with using the Razor MVC helper/extension:
#(Html.Kendo().AutoComplete()
.Name("userAutoComplete") // specifies the "id" attribute of the widget
.DataTextField("USERNAME")
.DataSource(source =>
{
source.Read(read =>
{
read.Url("/api/user");
})
.ServerFiltering(true); // if true, the DataSource will not filter the data on the client
}
)
)
and through directly through JS:
/// <reference path="kendo/kendo.aspnetmvc.min.js" />
/// <reference path="kendo/kendo.core.min.js" />
/// <reference path="kendo/kendo.autocomplete.min.js" />
/// <reference path="kendo/kendo.web.min.js" />
$(document).ready(function () {
// load up KendoUI
// gets data from /api/user
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "/api/user"
}
}
});
$("#userSearch").kendoAutoComplete({
dataSource: dataSource,
dataTextField: "USERNAME",
minLength: 3
});
$("#userSearch").on('input', function () {
console.log($("#userSearch").val());
});
}); // $(document).ready()
I'm sure this is something simple that I may be missing. I have tried both with the web and all js files.
Any assistance would be appreciated.
-- UPDATE --
The only real html missing from that content is the <input id="userAutoComplete" />
I created a brand new solution and a very simple view, based on one of the Kendo UI examples that gets JSON data from http://api.geonames.org, and getting the same error.
I thought that using the newest JS library (//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js may have been causing a problem so I tried the 1.7 lib. Same issue:
#using Kendo.Mvc.UI
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link rel="stylesheet" href="#Url.Content("~/Content/kendo.common.min.css")">
<link rel="stylesheet" href="#Url.Content("~/Content/kendo.default.min.css")">
<link rel="stylesheet" href="#Url.Content("~/Content/kendo.dataviz.min.css")">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script src="#Url.Content("~/Scripts/kendo.web.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo.aspnetmvc.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo.dataviz.min.js")"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#autoComplete").kendoAutoComplete({
minLength: 6,
dataTextField: "title",
filter: "contains",
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: "http://api.geonames.org/wikipediaSearchJSON",
data: {
q: function () {
return $("#autoComplete").data("kendoAutoComplete").value();
},
maxRows: 10,
username: "demo"
}
}
},
schema: {
data: "geonames"
}
}),
change: function () {
this.dataSource.read();
}
})
});
</script>
</head>
<body>
<div>
<input id="autoComplete"/>
</div>
</body>
</html>
-- UPDATE --
Using the code above, I went back and tried it again - it worked fine. After trying several more times, I experienced the same issue. This was due to the valid JSON data changing to the following:
{"status":{"message":"the daily limit of 30000 credits for demo has been exceeded. Please use an application specific account. Do not use the demo account for your application.","value":18}}
... which lead me to look at the formatting of the data coming from my API (looking at it in Fiddler:
Instead of:
JSON
---{... data...
it's
JSON
---$id=1
---$values
------{}
---------$id=2
---------CREATEDATETIME...
---------EMAIL=email#email.com
---------GROUPS
------------$id=...
------------$id=...
---------USERNAME=someusername
------{}
---------$id=4
.
.
.
So the error is caused by the array not being accessible where the it's expected - instead of the root, it's one level deep.
How do I get data binding to the one-level-deep rather than the root of the JSON object?
Thanks.
I had the same error with a ComboBox that I was using as an autocomplete. In my controller, the return statement was
return Json(model.ToDataSourceResult(dataSourceRequest), JsonRequestBehavior.AllowGet)
which I changed to
return Json(model, JsonRequestBehavior.AllowGet)
This provided the array at the root level instead of one level deep for me.
The solution for this was to traverse the data hierarchy by describing the result format.
Since the array is contained in $values, I used the following data source/schema definition:
// gets data from /api/user
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "/api/user"
}
},
schema: { // describe the result format
data: function(data) { // the data which the data source will be bound to is in the values field
console.log(data.$values);
return data.$values;
}
}
});
One thing that would be nice is to be able to add a data schema type in the Razor helper - which doesn't seem to be supported at this time.
Thus, the following still would not work:
#(Html.Kendo().AutoComplete()
.Name("userAutoComplete") // specifies the "id" attribute of the widget
.Filter("startswith")
.Placeholder("Type user name...")
.DataTextField("USERNAME")
.DataSource(source =>
{
source:
source.Read(read =>
{
read.Url("/api/user");
})
.ServerFiltering(true); // if true, the DataSource will not filter the data on the client
}
)
)
This worked for me:
var dataSource = new kendo.data.DataSource({
transport: {
read:
{
url: "api/dashboard"
}
},
schema: {
**data: function (data)
{
return [data];
}**
}
});
My response wasn't an array, i was returning from the server a response object like this:
{"Field1":0,"Field2":0,"Field3":0}
thanks "brittongr"...that worked for me too. but in my case it is not right, I was building a chart, a chart need an array of course, so instead of altering the schema by converting my Json data to an array I just returned from my action a list having one element. Something like this below.
Random rand = new Random();
int numIterations = 0;
numIterations = rand.Next(1, 1200);
List aux = new List<graphicDataItem>();
aux.Add(new graphicDataItem { ColumnTotal = 1200, ColumnActives = numIterations, ColumnInactives = 1200 - numIterations, ColumnApprovedByMembers = 250, ColumnApprovedByAssoc = 300, XAxisData = DateTime.Now.Year });
return Json(aux, JsonRequestBehavior.AllowGet);
I have "graphicDataItem" type defined on my Entities folder, but is easy to get by looking at the way it is instantiated within the code.
i change for this, and this work for me:
#(Html.Kendo().AutoComplete()
.Name("productAutoComplete") //The name of the autocomplete is mandatory. It specifies the "id" attribute of the widget.
.DataTextField("myfield") //Specifies which property of the Product to be used by the autocomplete.
.DataSource(source =>
{
source.Custom()
.Type("aspnetmvc-ajax")
.Transport(transport=>
{
transport.Read("MyAction", "Control");
})
.Schema(schema=>schema.Data("Data").Total("Total"))
.ServerFiltering(true); //If true the DataSource will not filter the data on the client.
})
)

Categories