jQuery almost winning me - UI Autocomplete " data undefined" - javascript

jQuery is almost winning me. I already saw lots and lots in StackOverflow answers and I can't find a solution to my problem.
I'm using jQuery UI Autocomplete in my project, and I need render a default message if no results is returned. I'm using "_renderItem", as you can see bellow.
var autocompleteDir = $("#search");
autocompleteDir.autocomplete({
source: function (request, response) {
populate(request.term, response);
result = $.ui.autocomplete.filter(result, request.term)
var item = {};
item.type = 'loading';
item.label = "Loading..";
item.value = "";
result.unshift(item)
response(result.slice(0, 10));
}
});
autocompleteDir.data("ui-autocomplete")._renderItem = function (ul, item) {
if (item.type === "loading") {
return $('<li></li>')
.data("ui-autocomplete-item", item)
.append("<div style='text-align: center;'>" + item.label + "</div>")
.appendTo(ul);
}
};
And here's what I'm getting on the console:
Uncaught TypeError: Cannot read property 'type' of null
Uncaught TypeError: Cannot call method 'data' of undefined
I'm using jQuery 1.10.2 and jQuery UI 1.10.3. Someone have a good idea?
Edit 1:
If I use this before "source":
response: function(event, ui){
if (ui.item.type === "loading"){
ui.content.push({label:"Loading",value:"Loading"})
}
}
I have the following error:
Cannot read property 'type' of undefined
If you help me solve this problem, can I use "response" to format and style my "loading" and my "none" result?
Edit 2
var populate = function(term, response) {
$.getJSON(
'<%= my_rails_path %>.json',
{search: term},
function(json) {
var result = [];
$.each(json, function(key, value) {
var item = {};
item.type = '';
item.label = value.name;
item.value = value.name;
result.push(item);
})
if (result.length == 0) {
var item = {};
item.type = "noResult";
item.label = "Create '" + term + "'";
item.value = term;
result.push(item)
}
response(result);
}
);
};
Edit 3
Now the problem is solved, but the label is literally showed, but I want that be rendered. See the code, you'll understand:
response: function(event, ui){
for(var i in ui.content){
if (ui.content[i].type==="loading"){
ui.content[i] = {
label:"<div style='text-align: center;'>Loading</div>",
value:""
}
}
}
}
Instead render "Loading" in the middle of the ui, all the string is showed to user (Loading).

You don't need to use _renderItem because jQuery provides an event that fires after the search returns and before the results are displayed, which you can modify to return a default value if the search return was empty. See the response event. You can do this instead:
autocompleteDir.autocomplete({
source: function (request, response) {
populate(request.term, response);
result = $.ui.autocomplete.filter(result, request.term)
var item = {};
item.type = 'loading';
item.label = "Loading..";
item.value = "";
result.unshift(item)
response(result.slice(0, 10));
},
response: function(event, ui){
if(ui.content.length === 0){
ui.content.push({label:"default",value:"value"}); /* modify to your liking */
}
}
});
Changing to this technique should solve both issues.
Edit:
In your third edit, if you want to change the value of a div somewhere else on the page, you need to do this in the response function yourself, not by returning the value in the function.
response: function(event, ui){
if(ui.content.length === 0){
$('#id-of-div-to-change').text("Loading");
}
}

Related

Select2 js - How to create a option when typing on search box

I'm using select2 for create an unlimited option for select, which means whenever you type anything in select2 search box it can create an option related to what is the select for. Now I'm using the code below, which when type down it creates more than 1 option that I need it for.
Please help me with this.
$('#form-label-category-exterior').select2({
matcher: function matchExterior(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.text.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
} else{
var newOpt = new Option(params.term.toString() + '外装', params.term.toString() + '外装', true, true);
$('#form-label-category-exterior').append(newOpt)
}
return null;
}
})
You can see the source of this feature from the link below. But as far as i understand you can only add an extra option here. So i think this is not what you want.
Source Link
So i have different solution if you want to use. The code that i wrote
it creates new option and delete old option that my code created. I tried to explain everything. I hope that you get the point.
$(document).ready(function() {
let oldValue = "";
$('#form-label-category-exterior').select2({
matcher: function matchExterior(params, data){
if ($.trim(params.term) === '') return data;
if (typeof data.text === 'undefined') return null;
if (data.text.indexOf(params.term) > -1) {
return $.extend({}, data, true);
} else {
const newText = params.term.toString() + '外装';
var newOpt = new Option(newText, newText);
$(`#form-label-category-exterior option[value="${oldValue}"]`).remove()
$('#form-label-category-exterior').append(newOpt)
const $return = data.id == params.term.slice(0, -1) + '外装'
oldValue = newText;
$('#form-label-category-exterior').trigger('change');
return $return ? {id: newText, text: newText } : null;
}
return null;
}
})
$('#form-label-category-exterior').on('select2:closing', function (e) {
oldValue = ""
});
});

Vuejs rendering one item less

I am new in Vuejs and I get an unexpected output in my app. What my app does is to search on YouTube API for channels, and then adding those channels in a list.
Then I like to render the list of the subscribed channels, but always I get one item less. Alway the last inserted item is missing from the rendered list, while the item exists in my data.
This is the rendered output:
If you see the right column, under the text box, has only one item rendered, while in my Vue console I have two items under the channels_info key:
Then if I try to append yet another one item in the list, the console will display 3 items while the HTML render will display 2, and so on.
My code is the following:
var setup = function () {
app = new Vue(
{
el : '#sml_app',
data : {
channel_name : '',
errors : [],
channels_found : {},
no_channels_found : true,
next_page_token : '',
prev_page_token : '',
page_token : '',
subscriptions : [],
channels_info : {},
subscriptions_counter: 1
},
methods: {
fetch_channel_info : function ($channel_id) {
var self = this;
var base_api_url = 'https://www.googleapis.com/youtube/v3/channels';
var query_params = {
'part' : 'snippet,contentDetails',
'key' : 'My-ApiKey',
'maxResults': 1,
'id' : $channel_id
};
var get_params = '';
for (var key in query_params) {
if (get_params != '') {
get_params += '&';
}
get_params += key + '=' + encodeURIComponent(query_params[key]);
}
get_params = '?' + get_params;
axios.get(base_api_url + get_params).then(
function (data) {
data = 'data' in data ? data.data : {};
if (
typeof undefined !== typeof data.items &&
typeof undefined !== typeof data.items[0] &&
typeof undefined === typeof self.channels_info[$channel_id]
) {
var snippet = data.items[0].snippet;
var $key = self.subscriptions_counter + '-' + $channel_id;
self.channels_info[$key] = snippet;
self.subscriptions_counter += 1;
}
}
).catch(
function () {
self.errors.push(
'No channel found matching this channel id.');
}
);
},
// ...
append_to_subscriptions: function ($channel_id) {
if (-1 === this.subscriptions.indexOf($channel_id)) {
this.subscriptions.push($channel_id);
this.fetch_channel_info($channel_id);
// Todo-merianos: Create an AJAX request to set the options in
// database
}
}
}
}
);
};
While my HTML side is like that:
<div class="subscription" v-for="subscription in channels_info">
<span v-text="subscription.title"></span>
</div>
Do you see anything in wrong ? I don't understand why I have that strange output :/
Any sugestion please?
You're appending a new property to an object. I recommend reading this relevant section of the Vue.js documentation regarding object change detection caveats. Specifically, you can use Vue.set(object, key, value) to ensure that your new object key is detected and becomes reactive.
So, instead of self.channels_info[$key] = snippet; you might instead do something like Vue.set(this.channels_info, $key, snippet);.
Definitely read through some more of the documentation. I'm certain that you'll find a lot of value in the rest of the information on this topic.

How can I save javascript variables locally?

I am a beginner in Javascript/Jquery and I am making a mobile web app using jquery mobile and jquery and I can't figure out how to display all my inputs in one place. No matter how many data I enter into the form it always displays the last entered .Please, any help?
$(document).ready(function() {
if(localStorage['linrval'],localStorage['linrdate']){
$('#inrhist').prepend('<div class="inrval">'+localStorage['linrdate']+ ' ---- ' +localStorage['linrval']+ '</div>');
};
$('#inrbtn').click(function(){
var inrval=$('input[name=user]').val();
var inrdate=$('input[name=dateinr]').val();
localStorage.setItem('linrval',inrval);
localStorage.setItem('linrdate',inrdate);
$('#inrhist').prepend('<div class="inrval">'+inrdate+ ' ---- ' +inrval+ '</div>');
});
Couple of things need to change here every time you need to add into array instead of you update the item value with same property. localStorage only supports strings.
$(document).ready(function() {
//localStorage.removeItem("users");
var userStr = localStorage.getItem('users');
if (userStr != null && userStr != undefined) {
var jsonObj = JSON.parse(userStr);
console.log("onload value", jsonObj);
$.each(jsonObj.items, function(i, item) {
$('#inrhist').prepend('<div class="inrval">'+item.user +'--'+item.dateinr+'</div>');
});
}
$('#inrbtn').click(function () {
var dataItems = { items: [] };
var inrval = $('input[name=user]').val();
var inrdate = $('input[name=dateinr]').val();
var item = { user: inrval, dateinr: inrdate };
var usersList = localStorage.getItem('users');
var jsonObj;
if (usersList == null) {
dataItems.items.push(item);
jsonObj = JSON.parse(JSON.stringify(dataItems));
}
else {
jsonObj = JSON.parse(usersList);
jsonObj.items.push(item);
}
jsonStr = JSON.stringify(jsonObj);
console.log(jsonStr);
localStorage.setItem("users", jsonStr);
$('#inrhist').prepend('<div class="inrval">' + inrdate + '--' + inrval + '</div>');
});
});
LIVE DEMO
You have this:
if(localStorage['linrval'],localStorage['linrdate']){...}
Such expression is true if and only if localStorage['linrdate'] is true. The value for localStorage['linrval'] is basically ignored.
Perhaps you want this:
if( localStorage['linrval'] || localStorage['linrdate'] ){...}
^^
You're also overwriting your localStorage values:
localStorage.setItem('linrval',inrval);
localStorage.setItem('linrdate',inrdate);

Why can't jQuery update array data before ajax post?

I am trying to create an array and get all values of a form submission and put them in that array. I need to do this because during the .each function of this code I must do additional encryption to all the values per client. This is a form with hundreds of fields that are changing. So it must be an array to work. I tried to do following and several other types like it in jQuery but no dice. Can anyone help? Thanks.
Edit: Posted my working solution. Thanks for the help.
Edit 2: Accept sabithpocker's answer as it allowed me to keep my key names.
var inputArray = {};
//jQuery(this).serializeArray() = [{name: "field1", value:"val1"}, {name:field2...}...]
jQuery(this).serializeArray().each(function(index, value) {
inputArray[value.name] = encrypt(value.value);
});
//now inputArray = [{name: "field1", value:"ENCRYPTED_val1"}, {name:field2...}...]
//now to form the POST message
postMessages = [];
$(inputArray).each(function(i,v){
postMessages.push(v.name + "=" + v.value);
});
postMessage = postMessages.join('&');
Chack serializeArray() to see the JSON array format.
http://jsfiddle.net/kv9U3/
So clearly the issue is that this in your case is not the array as you suppose. Please clarify what this pointer refers to, or just verify yourselves by doing a console.log(this)
As you updated your answer, in your case this pointer refers to the form you submitted, how do you want to iterate over the form? what are you trying to achieve with the each?
UPDATE
working fiddle with capitalizing instead of encrypting
http://jsfiddle.net/kv9U3/6/
$('#x').submit(function (e) {
e.preventDefault();
var inputArray = [];
console.log(jQuery(this).serializeArray());
jQuery(jQuery(this).serializeArray()).each(function (index, value) {
item = {};
item[value.name] = value.value.toUpperCase();
inputArray[index] = item;
});
console.log(inputArray);
postMessages = [];
$(inputArray).each(function (i, v) {
for(var k in v)
postMessages[i] = k + "=" + v[k];
console.log(i, v);
});
postMessage = postMessages.join('&');
console.log(postMessage);
return false;
});
The problem is that #cja_form won't list its fields using each. You can use serialize() instead:
inputArray = jQuery(this).serialize();
Further edition, if you need to edit each element, you can use this:
var input = {};
$(this).find('input, select, textarea').each(function(){
var element = $(this);
input[element.attr('name')] = element.val();
});
Full code
jQuery(document).ready(function($){
$("#cja_form").submit(function(event){
$("#submitapp").attr("disabled","disabled");
$("#cja_status").html('<div class="cja_pending">Please wait while we process your application.</div>');
var input = {};
$(this).find('input, select, textarea').each(function(){
var element = $(this);
input[element.attr('name')] = element.val();
});
$.post('../wp-content/plugins/coffey-jobapp/processes/public-form.php', input)
.success(function(result){
if (result.indexOf("success") === -1) {
$("#submitapp").removeAttr('disabled');
$("#cja_status").html('<div class="cja_fail">'+result+'</div>');
}
else {
page = document.URL;
if (page.indexOf('?') === -1) {
window.location = page + '?action=success';
}
else {
window.location = page + '&action=success';
}
}
})
.error(function(){
$("#submitapp").removeAttr('disabled');
$("#cja_status").html('<div class="cja_fail"><strong>Failed to submit article! Check your internet connection.</strong></div>');
});
event.preventDefault();
event.returnValue = false;
return false;
});
});
Original answer:
There are no associative arrays in javascript, you need a hash/object:
var input = {};
jQuery(this).each(function(k, v){
input[k] = v;
});
Here is my working solution. In this example it adds cat to all the entries and then sends it to the PHP page as an array. From there I access my array via $_POST['data']. I found this solution on http://blog.johnryding.com/post/1548511993/how-to-submit-javascript-arrays-through-jquery-ajax-call
jQuery(document).ready(function () {
jQuery("#cja_form").submit(function(event){
jQuery("#submitapp").attr("disabled","disabled");
jQuery("#cja_status").html('<div class="cja_pending">Please wait while we process your application.</div>');
var data = [];
jQuery.each(jQuery(this).serializeArray(), function(index, value) {
data[index] = value.value + "cat";
});
jQuery.post('../wp-content/plugins/coffey-jobapp/processes/public-form.php', {'data[]': data})
.success(function(result){
if (result.indexOf("success") === -1) {
jQuery("#submitapp").removeAttr('disabled');
jQuery("#cja_status").html('<div class="cja_fail">'+result+'</div>');
} else {
page = document.URL;
if(page.indexOf('?') === -1) {
window.location = page+'?action=success';
} else {
window.location = page+'&action=success';
}
}
})
.error(function(){
jQuery("#submitapp").removeAttr('disabled');
jQuery("#cja_status").html('<div class="cja_fail"><strong>Failed to submit article! Check your internet connection.</strong></div>');
});
event.preventDefault();
event.returnValue = false;
});
});

Handling no results in jquery autocomplete

Hey I'm trying to return a message when there are no results for the users current query! i know i need to tap into the keyup event, but it looks like the plugin is using it
This question is really out of date, anyways I'm working with the new jQuery UI 1.8.16, autocomplete is now pretty different:http://jqueryui.com/demos/autocomplete/#default
Anyways if you're trying to the do the same thing as the question asks, there is no more parse function, as far as I know there is no function that is called with the search results.
The way I managed to pull this off is by overriding the autocomplete's filter function - Note: this will affect all your autocompletes
$.ui.autocomplete.filter = function(array, term) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
var aryMatches = $.grep( array, function(value) {
return matcher.test(value.label || value.value || value);
});
if (aryMatches.length == 0){
aryMatches.push({
label: '<span class="info" style="font-style: italic;">no match found</span>',
value: null
});
}
return aryMatches;
};
The function is slightly modified from the source, the grep call is the same, but if there are no results I add an object with a value of null, then I override the select calls to check for a null value.
This gives you an effect where if you keep typing and no matches are found you get the 'no matches found' item in the dropdown, which is pretty cool.
To override the select calls see jQuery UI Autocomplete disable Select & Close events
$(this).data('autocomplete').menu.options.selected = function(oEvent, ui){
if ($(ui.item).data('item.autocomplete').value != null){
//your code here - remember to call close on your autocomplete after
}
};
Since I use this on all my autocompletes on a page, make sure you check if value is null first! Before you try to reference keys that aren't there.
You could try supplying a parse option (function to handle data parsing) and do what you need when no results are returned to parse.
This example assumes you're getting back an array of JSON objects that contain FullName and Address attributes.
$('#search').autocomplete( {
dataType: "json",
parse: function(data) {
var array = new Array();
if (!data || data.length == 0) {
// handle no data case specially
}
else {
for (var i = 0; i < data.length; ++i) {
var datum = data[i];
array[array.length] = {
data: datum,
value: data.FullName + ' ' + data.Address,
result: data.DisplayName
};
}
}
return array;
}
});
I'm using the following code for the same purpose (the message is shown in the autocomplete list):
success: function(data, status, xhr){
if(!data.length){
var result = [
{
label: 'There are no matches for your query: ' + response.term,
value: response.term
}
];
response(result);
}
else{
// normal response
}
}
You can also utilize the "response" event to examine this. Simple but powerful. http://api.jqueryui.com/autocomplete/#event-response
response: function (event, ui) {
if (ui.content.length == 0) {
//Display an alert or something similar since there are no results
}
},

Categories

Resources