Hello developers please help, updating value dynamically working perfect inside angularjs controller which is passing in html tag. But when I use scope variable inside function (within controller).
Working perfect when updating value outside function:
var app = angular.module('previewApp', []);
app.controller('previewCtrl', function ($scope) {
var $ = jQuery;
var targetValue = 0;
$scope.w = targetValue;
});
passing to html span tag:
<span style="width:{{w}}px;height:{{h}}px; background-color: white" class="item-inner">
But I am getting value using jQuery whenever input event trigger value is not updating inside function dynamically, in console value is updating perfectly (console.log('Width ' + targetValue);) but not in html tag:
function onChangeWidth(event) {
if (event.which != 8 && event.which != 0 && (event.which < 48 || event.which > 57)) {
isProcess = false;
}
const targetValue = $(this).closest('.gfield').find('input').val();
// $scope.width = ;
$scope.w = function () {
return targetValue;
};
$scope.docPPIWidth = ppi * +targetValue;
console.log('Width ' + targetValue);
$scope.w = targetValue; //updating w didn't work inside function
}
function onChangeHeight(event) {
const targetValueH = $(this).closest('.gfield').find('input').val();
if (event.which != 8 && event.which != 0 && (event.which < 48 || event.which > 57)) {
var isProcess = false;
}
$scope.docPPIHeight = ppi * targetValueH;
console.log('Height ' + targetValueH);
}
$('.width_check input').on('change click', onChangeWidth);
$('.height_check input').on('change click', onChangeHeight);
My first recommendation for you is to start thinking about how to prevent mixing JQuery and AngularJS, because eventually you will end up with "bug apocalypse".
I think what happens in your case is that Angular is not aware about JQuery triggered an event, so the digest loop is not triggered automatically.
Again, this solution can help you in that particular situation, but it is really not a good practice:
$scope.w = targetValue;
$scope.$digest();
After you change scope variable, let Angular know that it has to trigger digest loop to refresh view data by adding $scope.$digest(). This function will check only this component and its children scope data. In case if you need to check entire app, use $rootScope.$digest() (very poor decision from the performance standpoint).
Related
Is there a recommended way to unittest javascript in Laravel 5? I'm using the jQuery framework to do the search autocomplete task (code below). There's a lot of if blocks that I want to test but I'm not sure how to best test everything in Laravel 5. Any help is appreciated.
<script type="text/javascript">
(function($) {
var autocomplete_timer;
var ajax_request;
var autocomplete_delay = 50;
var search_keyword = "";
var max_result_count = 5;
var autocomplete_results = $('#autocomplete-results');
var autocomplete_results_list;
var default_report_url = '{{ route('report_summary_date_range_filter') }}';
var selected_autocomplete_item_index = 0;
var active_li = false;
var autocomplete_error = $('#autocomplete-error');
var redirect_state = false; //
$(document).ready(function () {
autocomplete_results_list = autocomplete_results.children('ul');
// Search Autocomplete
$("#search").keyup(function (e) {
search_keyword = $(this).val();
// If there is an existing XHR, abort it.
if (ajax_request) {
ajax_request.abort()
}
// Enable list iteration via keyboard
if (e.keyCode == 40 || e.keyCode == 38) {
ajax_request.abort();
var results_count = autocomplete_results_list.children('li').length;
if (e.keyCode == 40 && selected_autocomplete_item_index < results_count && active_li != false) {
selected_autocomplete_item_index++;
} else if (e.keyCode == 38 && selected_autocomplete_item_index > 0 && active_li != false) {
selected_autocomplete_item_index--;
}
active_li = autocomplete_results_list.children('li:eq(' + selected_autocomplete_item_index + ')');
if (active_li.length > 0) {
active_li.addClass('active');
autocomplete_results_list.children('li').not(active_li).removeClass('active');
$('#search').val(active_li.children('.autocomplete-ticker').text());
}
e.preventDefault();
return false;
}
// Clear the timer so we don't end up with dupes.
clearTimeout(autocomplete_timer);
// don't trigger ajax if user pressed enter/return key
// while a redirect is triggered
if (e.keyCode == 13 && redirect_state == true) {
return false;
}
if (search_keyword != '') {
// reset the index
selected_autocomplete_item_index = 0;
active_li = false;
// assign timer a new timeout
autocomplete_timer = setTimeout(function() {
ajax_request = $.ajax({
type: 'POST',
url: '/ajax/company/search/' + search_keyword,
data: {'_token': '{{ csrf_token() }}'},
success: function(response) {
if (response.count != 0) {
autocomplete_results.show();
autocomplete_results_list.empty();
autocomplete_error.hide();
var current_results = ((response.count > max_result_count) ? max_result_count : response.count);
for (var index = 0; index < current_results; index++) {
autocomplete_results_list.append(
'<li>' +
'<span class="autocomplete-ticker">' + response.results[index].ticker + '</span>' +
'<span class="autocomplete-company">' + response.results[index].name + '</span>' +
'</li>'
);
}
} else {
autocomplete_results_list.empty();
autocomplete_results.show();
autocomplete_error.show();
}
}
});
}, autocomplete_delay);
} else {
autocomplete_results.hide();
autocomplete_results_list.empty();
}
}).keydown(function (e) {
// prevent moving cursor to start of input field
// if the user presses the up arrow key
if (e.keyCode == 38) {
e.preventDefault();
}
});
// handle user clicking of an autocomplete item
autocomplete_results_list.on('click', 'li', function () {
var ticker = $(this).children('.autocomplete-ticker').text();
$('#search').val(ticker);
default_report_url = default_report_url.replace('%7Bticker%7D', ticker);
// redirect
$(location).attr('href', default_report_url);
redirect_state = true;
});
// if the user presses the return key while an autocomplete list
// is present, select the first item on the list and trigger a redirect
$('#searchbar form').submit(function (e) {
if ($('#search').val() != '') {
if (autocomplete_results_list.has('li').length > 0) {
autocomplete_results_list.children('li').first().addClass('active');
var ticker = autocomplete_results_list.children('li').first()
.children('.autocomplete-ticker').text().toUpperCase();
if (ticker != '') {
default_report_url = default_report_url.replace('%7Bticker%7D', ticker);
// redirect
$(location).attr('href', default_report_url);
redirect_state = true;
}
}
}
e.preventDefault();
});
});
$(document).click(function (e) {
// Hide autocomplete results if user clicked outside the search input field
// or the autocomplete listing
var container = $('#searchbar');
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
$('#autocomplete-results').hide();
$('#autocomplete-results ul').empty();
}
});
})(jQuery);
</script>
The choice of Laravel (or any other back-end framework/language/platform) is entirely irrelevant. This is purely front-end code. As such, you need to be using front-end testing tools for it.
However, even before you start thinking about unit testing this code, you need to do some re-coding so that it is actually testable in units. As things stand, you have a large monolithic blob of code there which cannot really be unit tested at all. You need to extract the functionality into discrete functions; the shorter the better.
Once you've done that, I'd suggest that the best starting point for your testing would be QUnit. This is a unit test framework developed by the jQuery foundation, and used for testing jQuery itself. Since your code has jQuery as a dependency, I'd suggest that this would probably be the best place to start. However there are numerous other testing frameworks for JavaScript, and you may want to investigate some of those as well.
Bear in mind that UI code (which most front-end JavaScript is) is notoriously difficult to write good quality unit tests for. You may find that functional testing -- ie automated end-user testing via a browser -- will serve you better. (indeed, you should be considering doing this kind of testing, even if you do also write unit tests for the JS code). For this, you will need an automation tool for the browser. The most well known one is Selenium, but you may also want to look into Sahi and also PhantomJS.
I am using the typeahead directive in AngularJS and it works fine. However, I would like to have a button outside of the input that when clicked would show the typeahead dropdown. Here is a snippet of what I am after...
<li class="input">
<input focus-me="click" ng-model="something"
typeahead="state for state in Suggestions | filter:$viewValue:stateComparator" typeahead-focus typeahead-focus-first="false" typeahead-on-select="updateTagInput(newTagName)">
Open
</li>
Ok, I am having an absolutely terrible time trying to create a JSFiddle or even a Plunkr for this, so I will just give you the code for this directive.
This directive originally comes from..
This epic Bootstrap library!
..and I stole it and played with it. If you would like to use it, you will need the "Bootstrap" (its really a subset of angular directives) library that I linked to. You can make your own subset of this library, but I am not entirely sure of all of the dependencies my directive has as I am using the entire library in my project. Basically, you need any directive that starts with "typeahead".
As you can see, I have named the directive wwTypeahead (that "ww" is for WebWanderer!). It is a very easy to use directive and it works just like the original.
<input
class="form-control"
type="text"
spellcheck="false"
ng-model="selection"
ng-trim="false"
placeholder="Search Here"
ww-typeahead="key as key.label for key in list"
typeahead-on-select="selectionMade($item, $model, $label)"
typeahead-min-length="0"
/>
The really important part to note is the attribute typeahead-min-length="0" which has really been the heart of many discussions online. I managed to make that work.
This directive is meant to take the place of the typeahead directive in the library I linked to. Your typeahead list will be shown on focus of your input box. No, the list does not show on the click of a button, but hopefully getting there will be baby-steps from here. If you need help implementing that, I will be happy to help.
/*
NOTE:
The following directive is a modification of the
Angular typeahead directive. The normal directives,
unfortunately, do not allow matching on 0 length values
and the user may want a returned list of all values during
the lack of input.
This directives was taken from ...
http://angular-ui.github.io/bootstrap/
..and modified.
*/
angular.module('ui.directives', []).directive('wwTypeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
function($compile, $parse, $q, $timeout, $document, $position, typeaheadParser)
{
var HOT_KEYS = [9, 13, 27, 38, 40];
return {
require:'ngModel',
link:function(originalScope, element, attrs, modelCtrl)
{
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before typeahead kicks-in
//var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
var testEval = originalScope.$eval(attrs.typeaheadMinLength);
var minSearch = !isNaN(parseFloat(testEval)) && isFinite(testEval) || 1;
//minimal wait time after last character typed before typehead kicks-in
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
//a callback executed when a match is selected
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
//INTERNAL VARIABLES
//model setter executed upon match selection
var $setModelValue = $parse(attrs.ngModel).assign;
//expressions used by typeahead
var parserResult = typeaheadParser.parse(attrs.cmcTypeahead);
//pop-up element used to display matches
var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
popUpEl.attr({
matches: 'matches',
active: 'activeIdx',
select: 'select(activeIdx)',
query: 'query',
position: 'position'
});
//custom item template
if(angular.isDefined(attrs.typeaheadTemplateUrl))
{
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
}
//create a child scope for the typeahead directive so we are not polluting original scope
//with typeahead-specific data (matches, query etc.)
var scope = originalScope.$new();
originalScope.$on('$destroy', function()
{
scope.$destroy();
});
var resetMatches = function()
{
scope.matches = [];
scope.activeIdx = -1;
};
var getMatchesAsync = function(inputValue)
{
var matchParsePrefix = originalScope.$eval(attrs.typeaheadParsePrefix);
var locals = {
$viewValue: inputValue.indexOf(matchParsePrefix) === 0 ? inputValue.substring(matchParsePrefix.length, (inputValue.length + 1)) : inputValue
};
isLoadingSetter(originalScope, true);
$q.when(parserResult.source(scope, locals)).then(function(matches)
{
//it might happen that several async queries were in progress if a user were typing fast
//but we are interested only in responses that correspond to the current view value
//if(matches && inputValue === modelCtrl.$viewValue)
/*
Ehh.. that didn't seem to work when I "cleared" the input box
*/
if(matches)
{
if(matches.length > 0)
{
scope.activeIdx = 0;
scope.matches.length = 0;
//transform labels
for(var i = 0; i < matches.length; i++)
{
locals[parserResult.itemName] = matches[i];
scope.matches.push({
label: parserResult.viewMapper(scope, locals),
model: matches[i]
});
}
scope.query = inputValue;
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
//due to other elements being rendered
scope.position = $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
}
else if(minSearch === 0)
{
resetMatches();//temp
}
else
{
resetMatches();
}
isLoadingSetter(originalScope, false);
}
}, function()
{
resetMatches();
isLoadingSetter(originalScope, false);
});
};
resetMatches();
/*
Can't figure out how to make this work...*/
if(attrs.hasOwnProperty('typeaheadBindMatchReloader'))
{
$parse(attrs.typeaheadBindMatchReloader).assign(scope, function()
{
getMatchesAsync(element[0].value);
});
}
//we need to propagate user's query so we can higlight matches
scope.query = undefined;
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutPromise;
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
modelCtrl.$parsers.unshift(function(inputValue)
{
resetMatches();
if((inputValue && inputValue.length >= minSearch)
|| minSearch === 0)
{
if(waitTime > 0)
{
if(timeoutPromise)
{
$timeout.cancel(timeoutPromise);//cancel previous timeout
}
timeoutPromise = $timeout(function()
{
getMatchesAsync(inputValue);
}, waitTime);
}
else
{
getMatchesAsync(inputValue);
}
}
if(isEditable)
{
return inputValue;
}
else
{
modelCtrl.$setValidity('editable', false);
return undefined;
}
});
modelCtrl.$formatters.push(function(modelValue)
{
var candidateViewValue, emptyViewValue;
var locals = {};
if(inputFormatter)
{
locals['$model'] = modelValue;
return inputFormatter(originalScope, locals);
}
else
{
//it might happen that we don't have enough info to properly render input value
//we need to check for this situation and simply return model value if we can't apply custom formatting
locals[parserResult.itemName] = modelValue;
candidateViewValue = parserResult.viewMapper(originalScope, locals);
locals[parserResult.itemName] = undefined;
emptyViewValue = parserResult.viewMapper(originalScope, locals);
return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
}
});
scope.select = function(activeIdx)
{
//called from within the $digest() cycle
var locals = {};
var model, item;
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
model = parserResult.modelMapper(originalScope, locals);
$setModelValue(originalScope, model);
modelCtrl.$setValidity('editable', true);
onSelectCallback(originalScope, {
$item: item,
$model: model,
$label: parserResult.viewMapper(originalScope, locals)
});
resetMatches();
//return focus to the input element if a mach was selected via a mouse click event
element[0].focus();
};
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
element.bind('keydown', function(evt)
{
//typeahead is open and an "interesting" key was pressed
if(scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1)
return;
evt.preventDefault();
if(evt.which === 40)
{
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
}
else if(evt.which === 38)
{
scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
}
else if(evt.which === 13 || evt.which === 9)
{
scope.$apply(function()
{
scope.select(scope.activeIdx);
});
}
else if(evt.which === 27)
{
evt.stopPropagation();
resetMatches();
scope.$digest();
}
});
// Keep reference to click handler to unbind it.
var dismissClickHandler = function(evt)
{
if(element[0] !== evt.target)
{
resetMatches();
scope.$digest();
}
else
{
getMatchesAsync(element[0].value);
}
};
$document.bind('click', dismissClickHandler);
originalScope.$on('$destroy', function()
{
$document.unbind('click', dismissClickHandler);
});
element.after($compile(popUpEl)(scope));
}
};
}]);
Call To Action:
Somebody PLEASE make a working example of this typeahead directive! I would forever be in debt to you! (well, not really but it would make me very happy)
DISCLAIMER:
I understand that this answer is in no way orthodox. I did not provide the askee (askee?) with a direct answer to the question, yet I did provide the tools that I believe are needed to get to his/her answer. I understand that I should spend the time to make a working example, but I am a very busy man and simply wished to share my work with the community, as I have seen this question asked too many times while I sit back and hold the answer. Please let me know if you have any issues, questions, or complications. I am happy to help.
Thanks!
<input
class="form-control"
spellcheck="false"
focus-me="click" ng-model="something"
ng-trim="false"
placeholder="Search Here"
uib-typeahead="key as key.label for key in list | filter:{label:$viewValue}"
typeahead-on-select="openTypeAhead($item, $model, $label)"
typeahead-min-length="0"
/>
in controller angularjs
$scope.openTypeAhead = ($item, $model, $label) =>{ console.log('arg =>',$item, $model, $label);}
I'm trying to develop functionality for input generated by jsf. I already have javascript code that uses the onkeypressed to prevent putting non-numeric values by user:
function validateNumberKey(event){
return (45<event.charCode && event.charCode<58 && event.shiftKey==false && DotTracker().prototype.wasPressed())
|| (event.keyCode==8) || (event.keyCode==9)
}
the code that findsout if user pressed dot is here:
function DotTracker() {
var dotPressed = false;
this.wasPressed = function() {
var currentDot = dotPressed;
dotPressed = true;
return currentDot;
};
}
And simplified input reference:
<input type="text" id="inpurCP" onfocus="new DotTracker()" onkeypressed="validateNumberKey(event)"/>
I'm pretty sure that something goes wrong with calling wasPressed() method. But I'm not quite familiar with javascript though...
Ok I solve the problem. Instead of using DotTracker(). I created another function:
function wasDotPressed(event) {
var index = -1;
if(event.charCode == 46) {
var currentValue = event.currentTarget.value;
index = currentValue.indexOf('.');
}
return index == -1;
}
and add the relevant line of code to the validateNumberKey(event):
function validateNumberKey(event){
return (45<event.charCode && event.charCode<58 && event.shiftKey==false && wasDotPressed(event))
|| (event.keyCode==8) || (event.keyCode==9)
}
And finally it works :). Hope that it help anyone that is struggling such problem.
I was trying on the basics of Jquery plugin and the prototype concept, but ended up in an unusual behavior.
HTML :
<div>
<span>
<textarea>Text Area with 500 characters. Adding Some text.</textarea>
<span class="cl"></span>
</span>
<span>
<textarea>Text Area with 100 characters</textarea>
<span class="cl"></span>
</span>
</div>
JQuery :
(function ($) {
var tisCharsLeftCntxt = null;
function fnCharsLeft(ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
tisCharsLeftCntxt = this;
this.fnInit();
}
fnCharsLeft.prototype = {
fnInit: function () {
tisCharsLeftCntxt.fnUpdateRemainingChars();
tisCharsLeftCntxt.jqe.keyup(function (event) {
key = event.keyCode ? event.keyCode : event.which;
if ((37 != key) && (38 != key) && (39 != key) && (40 != key)) {
tisCharsLeftCntxt.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = tisCharsLeftCntxt.maxChars - tisCharsLeftCntxt.jqe.val().length,
jqeDestToUpdt = tisCharsLeftCntxt.jqe.siblings('.cl');
charsLft = (charsLft < 0) ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + tisCharsLeftCntxt.maxChars + ' characters');
} else {
tisCharsLeftCntxt.jqe.val(tisCharsLeftCntxt.jqe.val()
.substring(0, tisCharsLeftCntxt.maxChars));
tisCharsLeftCntxt.jqe.scrollTop(tisCharsLeftCntxt.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + tisCharsLeftCntxt.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
})(window.jQuery);
$('div span:nth-child(1) textarea').fnCharsLeftPlgn({maxChars: 500});
$('div span:nth-child(2) textarea').fnCharsLeftPlgn({maxChars: 100});
Fiddle :
http://jsfiddle.net/5UQ4D/ & http://jsfiddle.net/5UQ4D/1/
Requirement is, the plugin should show the number of characters that can be added in a text-area. If there is only one text-area in a page this is working good. But if there are more than one, only the text-area which is last associated with the plugin is working properly.
With respect to code here, In both the text-area number of characters left is updated correctly during initialization (only for the first time). But later when the text area content is changed, only the second with 100 chars (or the most recent text-area associated with the plugin) is working properly.
Seems like, I'm failing to restrict the plugin context independently to a text-area. Please Advice,..
Problem 1:
As mentioned in the comments, you're creating a variable named tisCharsLeftCntxt outside of the other contexts, then assigning this to it in your constructor. Every time you run your plugin you stomp on tisCharsLeftCntxt with a new this.
There is no reason to use a reference to this in the wholesale fashion in which you have. There is only one place in your code where the scope changes such that this is no longer your instance. That place is inside of the keyup event handling function. You should localize your aliasing of this to just the method which contains that event handler.
Problem 2:
I believe another part of your problem (this would be seen if you ran the plugin against a selector which matched more than one element) is inside of the plugin function (the one which lives off of $.fn).
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
It should be:
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data("charsleft", new fnCharsLeft(this, genStngs));
});
};
When directly inside of a method which has been added to the jQuery prototype ($.fn), this refers to the entirety of the current collection, not an element. A plugin should each itself in order to run element specific logic against its individual members.
Without using .each() you are calling .data() against an entire collection, setting all of their charsleft data properties to the one instance of fnCharsLeft. By using .each() you create a new instance of fnCharsLeft for each of the elements in the collection.
Since the .each() then returns the collection, and a plugin should be chainable, you simply return it.
A rule of thumb is that if you're passing this into the jQuery factory ($()) directly inside of a plugin, function then you're doing something wrong since it is already the collection. As a second rule of thumb, almost all plugin definitions except those which are intended to return info about an element (such as .val(), .html(), or .text() when not given a param) should start with return this.each(function() {...
Solutions:
Bringing those changes together results in this fiddle: http://jsfiddle.net/5UQ4D/4/
And this code:
(function ($) {
var fnCharsLeft = function (ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
this.fnInit();
};
fnCharsLeft.prototype = {
fnInit: function () {
var instance = this;
this.fnUpdateRemainingChars();
this.jqe.on('keyup', function (e) {
key = e.keyCode ? e.keyCode : e.which;
if (37 != key && 38 != key && 39 != key && 40 != key) {
instance.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = this.maxChars - this.jqe.val().length,
jqeDestToUpdt = this.jqe.siblings('.cl');
charsLft = charsLft < 0 ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + this.maxChars + ' characters');
} else {
this.jqe
.val(this.jqe.val().substring(0, this.maxChars))
.scrollTop(this.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + this.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data('charsleft', new fnCharsLeft(this, genStngs));
});
};
}(window.jQuery));
I want to add a autocomplete function to a site and found this guide which uses some js code which works really nice for one textbox: http://www.sks.com.np/article/9/ajax-autocomplete-using-php-mysql.html
However when trying to add multiple autocompletes only the last tetbox will work since it is the last one set.
Here is the function that sets the variables for the js script
function setAutoComplete(field_id, results_id, get_url)
{
// initialize vars
acSearchId = "#" + field_id;
acResultsId = "#" + results_id;
acURL = get_url;
// create the results div
$("#auto").append('<div id="' + results_id + '"></div>');
// register mostly used vars
acSearchField = $(acSearchId);
acResultsDiv = $(acResultsId);
// reposition div
repositionResultsDiv();
// on blur listener
acSearchField.blur(function(){ setTimeout("clearAutoComplete()", 200) });
// on key up listener
acSearchField.keyup(function (e) {
// get keyCode (window.event is for IE)
var keyCode = e.keyCode || window.event.keyCode;
var lastVal = acSearchField.val();
// check an treat up and down arrows
if(updownArrow(keyCode)){
return;
}
// check for an ENTER or ESC
if(keyCode == 13 || keyCode == 27){
clearAutoComplete();
return;
}
// if is text, call with delay
setTimeout(function () {autoComplete(lastVal)}, acDelay);
});
}
For one textbox I can call the function like this
$(function(){
setAutoComplete("field", "fieldSuggest", "/functions/autocomplete.php?part=");
});
However when using multiple textboxes I am unsure how I should go about doing this, here is something I did try but it did not work
$('#f1').focus(function (e) {
setAutoComplete("f1", "fSuggest1", "/functions/autocomplete.php?q1=");
}
$('#f2').focus(function (e) {
setAutoComplete("f2", "fSuggest2", "/functions/autocomplete.php?q2=");
}
Thanks for your help.
You should be using classes to make your function work in more than one element on the same page. Just drop the fixed ID's and do a forEach to target every single element with that class.