I'm attempting to use jQuery's autocomplete feature, and after reading several posts I still have two questions:
1) I've gotten autocomplete to work with the code posted at the bottom, however I need the array titled "data" to be filled from our database. I've been trying to use different methods to fill this via AJAX. I tried using $.get and $.ajax. What is the correct syntax to accomplish this?
2) This array will be big, I will have 60,000 plus values if I just fill the array once. I was wondering if it's possible to perform an AJAX request to fill the array every-time the user enters a new letter? Is this better to do, or just fill the array with all values at once? By better, which taxes the system less?
//This code works
<script type="text/javascript">
$(document).ready(function(){
var data = "Facebook Gowalla Foursquare".split(" ");
$("#search_company").autocomplete(data);
});
</script>
//display company live search
echo('<form id="form" method="post" action="competitor_unlink.php" onsubmit="return">');
echo('Company: <input id="search_company"/>');
echo('<br/>');
echo('<button type="submit" value="Submit">Submit</button>');
echo('</form>');
Look at this demo - it's what you want to do (get data using ajax):
http://jqueryui.com/demos/autocomplete/#remote
You can pull data in from a local
and/or a remote source: Local is good
for small data sets (like an address
book with 50 entries), remote is
necessary for big data sets, like a
database with hundreds or millions of
entries to select from.
Autocomplete can be customized to work
with various data sources, by just
specifying the source option. A data
source can be:
an Array with local data a String,
specifying a URL a Callback The local
data can be a simple Array of Strings,
or it contains Objects for each item
in the array, with either a label or
value property or both. The label
property is displayed in the
suggestion menu. The value will be
inserted into the input element after
the user selected something from the
menu. If just one property is
specified, it will be used for both,
eg. if you provide only
value-properties, the value will also
be used as the label.
When a String is used, the
Autocomplete plugin expects that
string to point to a URL resource that
will return JSON data. It can be on
the same host or on a different one
(must provide JSONP). The request
parameter "term" gets added to that
URL. The data itself can be in the
same format as the local data
described above.
The third variation, the callback,
provides the most flexibility, and can
be used to connect any data source to
Autocomplete. The callback gets two
arguments:
1) A request object, with a single
property called "term", which refers
to the value currently in the text
input. For example, when the user
entered "new yo" in a city field, the
Autocomplete term will equal "new yo".
2) A response callback, which expects
a single argument to contain the data
to suggest to the user. This data
should be filtered based on the
provided term, and can be in any of
the formats described above for simple
local data (String-Array or
Object-Array with label/value/both
properties). It's important when
providing a custom source callback to
handle errors during the request. You
must always call the response callback
even if you encounter an error. This
ensures that the widget always has the
correct state.
Here's an example of how to specify a URL that will return the results from the database as JSON using the jQuery UI autocomplete plugin.
$("#search_company").autocomplete({
source: "/Search", // <-- URL of the page you want to do the processing server-side
minLength: 4 // <-- don't try to run the search until the user enters at least 4 chars
});
Autocomplete will automatically append a querystring parameter named "term" to the URL so your search page will need to expect that. Not sure what server technology you're using but since I'm a .NET developer here's an example in ASP.NET MVC :)
public ActionResult Search(string term) {
var results = db.Search(term); // <-- this is where you query your DB
var jqItems = new List<jQueryUIAutoCompleteItem>();
foreach (var item in results) {
jqItems.Add(new jQueryUIAutoCompleteItem() {
value = item.CompanyId.ToString(),
id = item.CompanyId.ToString(),
label = item.CompanyName
});
}
return Json(jqItems.ToArray(), JsonRequestBehavior.AllowGet);
}
jQueryUIAutoCompleteItem is just a data container that represents the JSON format that the autocomplete plugin expects.
public class jQueryUIAutoCompleteItem {
public string value { get; set; }
public string label { get; set; }
public string id { get; set; }
}
You're correct that sending the whole 60,000-record list to the client's machine doesn't sound like the best solution. You'll notice that Google only shows you a handful of the most popular matches in its autocomplete, as to many other websites.
You could shorten the list by waiting for the user to type two or three letters instead of searching on the first one.
You could do page chunking in the list (it goes by various names). That is, only return the top 10 or 15 matches. The user can get more of the list by scrolling or by clicking on a "Show More Results" link. You have to write (or search for) all the javascript code for this, of course.
It might be a bit late to this post but for others that find it. I created a plugin for jquery and the jqueryui autocomplete control that works with Foursquare. You can read the post and download the plugin at Foursquare Autocomplete Plugin
Related
I have a simple chat application that I built and I want to be able to display user uploaded images (locally hosted) next to the user names on the channel's html page. Currently, I am using presence to track the users who are logged into the channel etc. I was able to override the fetch/2 function with the understanding that it would allow me to add a couple map fields to the :metas symbol with user model data.
From what I can tell based on extensive IO.inspecting of different parts of each of the functions; fetch/2, handle_info/2, and some console.logging on my JS layer, the fetch/2 function is not actually getting any data out of the database nor is it assigning it to the :metas map.
here is my current fetch/2 function:
def fetch(_topic, entries) do
query =
from u in User,
where: u.id in ^Map.keys(entries),
select: {u.id, u}
users = query |> Repo.all |> Enum.into(%{})
for {key, %{metas: metas}} <- entries, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
It is basically ripped directly from the documentation. In Theory, the function above should query my User Model and grab all of the user data based on the User.id that is being passed to it through the entries map. Users[keys] comes back as empty despite users being a full map of my User model.
Also, according to the documentation, the query is only supposed to run on join so as not to overload the DB but it seems to run 4-5 times every time I refresh the page. Another thing to note, is that the user.id inside of entries seems to be a string type. Im not sure if this is important, I've tried passing a integer from the JS layer and also using Interger.parse from the actual fetch/2 function to change this to no avail.
When I inspect the users map I get this:
{"1" => %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "test#test.com", encrypt_pass: "$pbkdf2-
sha512$160000$ebfY956TgIXhEAF.mqLJAg$QWzBubfeiy4Xrf.EsFiU0jEZAuKvV4ZO5a
8QpeFr817C61DuaNfyo56WWzj6jak2homCFWAINbPrFtCSXUPWTw", gravatar: %
{file_name: "logo.png", updated_at: #Ecto.DateTime<2017-04-20 22:00:08>},
id: 1, inserted_at: ~N[2017-04-20 22:00:09.071000], password: nil,
updated_at: ~N[2017-04-20 22:00:09.090000]}}
My users[key] returns an empty map like this %{} and converting the input into an integer throws an error, (Poison.EncodeError) unable to encode value: {nil, "users"} if i convert it inside of the elixir code, and where: u.id in ^["undefined"], select: {u.id, u} (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3 – if I convert it from the JS layer.
The original fetch/2 output is an array with online_at: 1492764577562 and phx_ref: "OAyzaGE82xc=" in the :metas map and my user id or email in the users var.
What is it that I am missing here? I know that the fetch/2 function only executes as a callback to the Presence.list/1 function which I am calling in my handle_info/2 channel function. I am also calling Presence.list in my JS layer and mapping it to my presences so that I can produce the list of usernames in the HTML. Am I just misunderstanding how this works or is there some other more simple way that I should be going about this? If you need to see more code I can supply more.
edit: I have a much better understanding of what is happening here. my entries map is actually this:
%{"1" => %{metas: [%{online_at: 1492798247818, phx_ref: "ELHwA+gWF+0="}]}}
So basically, the string for the user id, "1" is being mapped onto the metas map. When I try to just take that key out of the map with the Map.keys(entries) function it isn't able to pull anything out of the DB because it is a string, however, when I change it to an integer from the JavaScript side it throws an error because for whatever reason phoenix is expecting that key to be a string type. Strangely enough if I change the id from an id to an email and try to query the db with the email it doesn't work either. despite the email in the Database being string and the metas map expecting a string key for the entries map.
I am going to rebuild this channel part of the app from the ground up and see what is causing this problem. Then I will come back and see if I was able to fix the error.
You should validate the keys in the entries map first.
ids = Map.keys(entries)
true = Enum.all?(ids, &is_integer/1)
Ecto will convert strings to integers when interpolating into a query:
iex(40)> Ecto.Query.from(u in Users, where: u.id in ^[1, 2, "3", nil], select: u.id) |> Repo.all()
outputs the following debug log:
[debug] QUERY OK source="users" db=0.8ms
SELECT u0."id" FROM "users" AS r0 WHERE (r0."id" = ANY($1)) [[1, 2, 3, nil]]
Notice it coerced the string "3" to an integer and allowed the nil.
However a map will not be so kind:
iex(42)> users = %{1 => %{name: "joe"}, 2 => %{name: "jill"}}
%{1 => %{name: "joe"}, 2 => %{name: "jill"}}
iex(43)> users["1"]
nil
So in the code where you are using the keys from entries for database lookups and map lookups, it could be producing different results.
I've already figured out that the problem has very little to do with my fetch/2 function itself, rather, it had to do with my implementation of the presence module and channel in this case. Basically, the fetch/2 function was being called 4 times every time some one entered the chat room and two out of the four times it was being called with an empty list value [].
Obviously, you can't query a Ecto model with an empty list so it was throwing an error in that case as well. I tried putting guards on the fetch function to filter out the empty list calls but it would not show me the metas map data that I was looking for even when the query succeeded.
Also, the other main problem was my implementation or lack of implementation of a token. I wouldn't have to pass around the user model data through fetch function metas map if I was using a token for joining the chat room rather then just a user (aka the just a username). After making that realization, I was able to successfully connect the user model data with the channel and show it through the JS layer and ultimately, put it on the client.
Anyways guys, thanks for the suggestions. You may not have answered the question (It was my fault for asking the wrong question), but you certainly helped me get there. And also gave me the tools to form a much better understanding of the framework in general on the way.
If/When I have any more questions, I will make sure that I am asking the correct questions before posting them to stack overflow, that way I wont be wasting time.
Let's imagine a situation like this:
I have an ViewBag dynamic object which is basically a list fille with some results;
Scenario 1:
User 1 comes in and fills the ViewBag.Products object with a list of 50 items inside;
Scenario 2:
User 2 comes in and fills ViewBag.Products object with a list of 50 NEW items which ARE DIFFERENT than the previous 50 ones of a user 1.
Now when the both users get displayed results onto their page, which is located at /Analyze/Index <- view
I enable them so that they can sort out that list by a certain property of a class which is located inside the object like this:
public JsonResult GetSortedBySales()
{
var list = lista.OrderByDescending(x => x.SaleNumber).ToList();
return Json(list, JsonRequestBehavior.AllowGet);
}
public JsonResult GetSortedByFeedback()
{
var list = lista.OrderByDescending(x => x.Feedback).ToList();
return Json(list, JsonRequestBehavior.AllowGet);
}
As you can see this produces an issue like this:
The last user that added it's own items to the list are the items which will be shown to User 1 when he tries to sort out the list, since the list is now filled with User #2's items...
The list is filled with the items from the eBay API, therefore I cannot guarantee the integrity and uniqueness of the data to each user...
What I thought I can do here?? Is that I can store these items from the list somehow into the local jquery array and then perform the sorting from that local array, so that each jquery array is local to each user in their browser and no mixing of data is done...
Do you guys understand me what I'm trying to achieve here?
I apologize if my English is bad, I've tried my best to explain the issue that I have.
Edit: here is more data on what I'm trying to achieve
Basically I have a form where users perform a search of ebay items based on a certain keyword. After the search via http request I display the results to them in a manner where they can select all of those products in a table and then perform analyzing of those selected products.
Then they are transferred with another page with the analyzed data and I display the results to them in the list object called "lista".
The "lista" list is always filled differently based on what the user searches on the page via the process that I just explained above.
So the "lista" object is always filled with the new data and when the user performs sorting of data in list "lista" they are always displayed differently if 2 users perform analyzing of data like in scenario 1 and 2 that I explained above.
Does this helps?
Edit 2:
Here is a graphical explanation of what I ment
Step 1:
Step 2:
P.S. the "lista" list is declared as static, is that what's causing the issue?
Edit again:
Okay so guys I've found a way so that the data isn't changed when I sort it. Instead of doing a jQuery post, I perform sorting of data based on two extra attributes that I added into my table tr - sales and feedback and then sorted it like following:
$(".feedbackClick").click(function() {
var $wrapper = $('#tableSellers');
$wrapper.find('.test').sort(function(a, b) {
return +$(b).attr('feedback') - +$(a).attr('feedback');
}).appendTo($wrapper);
});
This sorts the data locally in jQuery thus no data is lost at the time when multiple users perform a search.
P.S. the "lista" list is declared as static, is that what's causing the issue?
Yes. That is the problem.
You should almost never be using static variables in a web site, as those variables will end up being shared across all users.
So I am using jQuery and have setup the jquery cookie plugin.
I have 4 drop down lists on my page, and I want to save the user's selections in a cookie, so when they come back to the page I automatically pre-select their previous selections.
I added a class to all my drop downs "ddl-cookie", and I was just thinking if I could somehow loop through all the drop down lists using the class, and save the selection and also loop to set the selections when the user returns to the page.
$(".ddl-cookie").each(function() {
});
It seems that given a cookie name, I can save a single key/value in the cookie.
So I'm guessing the only way for me to do this would be to have a comma separated list of drop down list names and values (selection value)?
You are correct. Cookies are intended to store a single piece of data, so the most common way to handle this is to serialize your data into an easy to retrieve format. That format is up to you, but you might use something like:
field_1=value1&field_2=value&...
You might want to also encode this data--remember that cookies are transferred as part of the request header. The pseudo code would go something like this:
// Store the data, using your own defined methods
data = serialize_data(data);
data = encode_data(data);
cookie = data;
// Retrieve the data using your own defined methods
data = cookie;
data = unencode_data(data)
data = deserialize_data(data)
I'm developing a portlet for WebSphere Portal 6.1, with JSP/JSTL, pure javascript, no AJAX frameworks, with a JSP that shows a send feedback form and, when submitted, redirects to another JSP to show the user the success of the operation.
I use javascript to get the values of the form fields by using document.getElementById() function. For example:
var valorAsunto = document.getElementById("asunto").value;
where "asunto" is the ID of a text field in my form. Also my form has the following structure:
<form name="formularioCorreo" id="formularioCorreo" method="post" action="<portlet:renderURL><portlet:param name="nextTask" value="sendFeedback"/></portlet:renderURL>">
That works OK, but I'm having trouble when trying to build the <portlet:renderURL> tag from that javascript values: when I try to concatenate a string for the renderURL and then reassign to form action like this:
var valorAction = '<portlet:renderURL><portlet:param name="nextTask" value="sendFeedback"/><portlet:param name="asunto" value="'+valorAsunto+'"/></portlet:renderURL>';
document.formularioCorreo.action = valorAction;
document.formularioCorreo.submit();
The resulting string, when application is deployed, has the structure:
/wps/myportal/<portletpath>/!ut/p/c5/<a very long random sequence of
numbers and letters>/
So one can't figure out where the parameter values are, but if I print the assigned values it shows something like:
asunto: '+valorAsunto+'
instead of
asunto: this is a sample subject
I've been trying to use some other ways to concatenate the string; for instance with a StringBuffer, as shown on http://www.java2s.com/Tutorial/JavaScript/0120__String/StringBufferbasedonarray.htm
and also javascript functions like encodeURI()/decodeURI(), replace(), etc. but I just can't get either the URL with the right parameter values or the URL encoded in the structure shown above (the one with the long sequence of chars).
Sometimes I manage to get the right parameter values, by manually replacing in the valorAction assignation all the "<" for "<" and all the ">" for ">" before the concatenation, and then doing the following:
var valorAction = valorAction.replace(/</g,"<").replace(/>/g,">");
Then I get the following string:
<portlet:renderURL><portlet:param name="nextTask" value="sendFeedback"/><portlet:param name="asunto" value="this is a sample subject"/></portlet:renderURL>
which is OK, but when it has to redirect to the results page it shows an error like this
Error 404: EJPEI0088E: The resource <portlet:renderURL><portlet:param
name="nextTask" value="sendFeedback"/><portlet:param name="asunto"
value="this is a sample subject"/></portlet:renderURL> could not be
found.
Does someone know how to transform that string to the right format to be rendered?
Does someone know any other way to "inject" that parameter values to the renderURL?
I'd like to know also if it is possible to pass that parameter values from javascript to JSP so I could put that values in a HashMap of parameters to use with the PortletURLHelper.generateSinglePortletRenderURL() method, in case the former is not possible.
Thank you.
Update 1:
In my doView() I use the following, in order to make the redirection:
String targetJsp = "/_Feedback/jsp/html/FeedbackPortletView.jsp";
String nextTask = request.getParameter("nextTask");
//(... I have omitted code to conditionally select targetJsp value, according to nextTask value ...)
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(targetJsp);
rd.include(request, response);
This is just a new JSP inside my portlet, not a different portal page. I do use request.getParameter() to get the values for my form fields from my doview():
String subjectFeedback = request.getParameter("asunto");
String bodyFeedback = request.getParameter("mensaje");
String emailFeedback = request.getParameter("emailFeedback");
I don't see the need to include hidden fields if my form has the fields named above. In fact, what I'm trying to do is to pass the values the user entered in these fields as request parameters, but the values I get by this means are the following:
subjectFeedback: "'+valorAsunto+'"
bodyFeedback: "'+valorMensaje+'"
emailFeedback: "'+valorEmailFeedback+'"
I get the above values when using concatenation by "+"; when I use StringBuffer I get the following values:
subjectFeedback: "'); buffer.append(valorAsunto); buffer.append('"
bodyFeedback: "'); buffer.append(valorMensaje); buffer.append('"
emailFeedback: "'); buffer.append(valorEmailFeedback); buffer.append('"
Does someone know any other way to "inject" that parameter values to the renderURL?
There are two IBM guides on that topic.
Portal 6.1 and 7.0 Advanced URL Generation Helper classes
How to create a link to a portlet (Standard API) that passes parameters to that portlet
How are you redirecting to the other page? Is it a different portal page or just a new JSP page inside your portlet?
You don't need to inject any parameters to the render URL. Have a form whose action targets to a renderURL. Now to pass information to your portlet's doView() method, you can have hidden fields in the form ,then populate them using JavaScript and then submit the form. In the doView() method, you can use request.getParameter() to get the parameters.
Well, sometimes the most obvious things happen to be the way to the solutions.
I was too busy trying to find elaborated causes for that situation that I did not checked for this at all:
My form fields were correctly identified by different id, but they weren't set their name properties.
With the help of a work partner we could figure out that, so assigning the same value of id for name on each form field did the trick.
So, I ended up skipping that reassigning action thing, because the field values are being set as request parameters, as it should be.
Thanks for the help.
Here's my issue:
Client(s) give me separate JS files which will run a check of some sort on the user's system (Browser type, are cookies enabled?, etc.) and a list of acceptable values to be returned from this check.
I want to run through each JS file when a user visits the site and compare the results to the list of acceptable values, then alert the user if they pass these requirements or not.
I'm currently using RegisterClientScriptBlock() to add the JS to the client's page, so it's being run, but I'm having issues getting the result value from the JS back to ASP.NET in order to do the comparison.
I've tried using hidden fields that the JS will dump the value to and ASP.NET will read from, but I'm having difficulty generating the hidden fields on demand (since I have no idea how many Js files the client could have) and have them work in the ASP.NET code.
Any help, or suggestions in the right direction would be awesome, thanks!
What I would do is have the results be an array of KeyValuePair objects that you would then serialize to JSON. So you create the javascript object type like so:
function KeyValuePair(key, value){
this.Key = key;
this.Value = value;
}
Then you would build up an array of KeyValuePairs like so:
//This array is declared in the global scope
var ValueArray = new Array();
function someFunction(){
//this assumes that the key and value variables are created earlier in the function
var valueToStore = new KeyValuePair(key, value);
ValueArray[ValueArray.length] = valueToStore;
}
So at the point when you are done with all your checks you would use the json2 serializer to serialize the array to json for storage in your hidden field.
var jsonToSaveToHiddenField = JSON.stringify(ValueArray);
//Logic to store resulting json and trigger the serverside evaluation here
On the server side you would use JavascriptSerializer to deserialize your json to an array of KeyValuePairs. Here is the msdn doc on that: JavaScriptSerializer Class Reference
So with this approach you only need one hidden field. So you don't need to dynamically create it which should simplify the server side retrieval quite a bit.
The above should work with minimal changes however I haven't run this through a compiler so there might be some minor syntax errors preset.