Angular.js multiple attribute directives working independently - angularjs

I have a directive, which I'm using as an attribute. The directive adds and removes a class, which triggers a css animation to fade the div in and out. I have this in multiple locations on my page; however, once the first div picks up the value, the rest of the divs(which are out of view), pick up the value as well. How would I make these directives work independently?
Directive:
.directive("scroll", function ($window) {
return function (scope, element, attrs) {
function getScrollOffsets(w) {
// Use the specified window or the current window if no argument
w = w || window;
// This works for all browsers except IE versions 8 and before
if (w.pageXOffset != null) return {
x: w.pageXOffset,
y: w.pageYOffset
};
}
angular.element($window).bind("scroll", function (e) {
var offset = getScrollOffsets($window);
if (offset.y >= 10) {
e.preventDefault();
e.stopPropagation();
element.removeClass('not-in-view');
element.addClass('in-view');
} else {
e.preventDefault();
e.stopPropagation();
element.removeClass('in-view');
element.addClass('not-in-view');
}
scope.$apply();
});
};
});
HTML:
<div class="sidebar col-md-4" scroll>
<h1>Content</h1>
</div>
<div class="sidebar col-md-4" scroll>
<h1>More Content</h1>
</div>

You should check if the element is visible in viewport by
function isElementInViewport (el) {
//special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
as How to tell if a DOM element is visible in the current viewport?
so that
angular.element($window).bind("scroll", function (e) {
if (isElementInViewport(element)) {
e.preventDefault();
e.stopPropagation();
element.removeClass('not-in-view');
element.addClass('in-view');
} else {
e.preventDefault();
e.stopPropagation();
element.removeClass('in-view');
element.addClass('not-in-view');
}
scope.$apply();
});

Here is my final solution thanks to #kwan, I tweaked it a bit.
.directive("scroll", function ($window) {
return{
scope:true,
link: function (scope, el, attrs) {
function isElementInViewport (el) {
//special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}
var height = $(el).height();
var rect = el.getBoundingClientRect();
return (
rect.top >= -(height / 1.25) &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)+ (height / 1.25) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
angular.element($window).bind("scroll", function (e) {
if (isElementInViewport(el)) {
e.preventDefault();
e.stopPropagation();
el.removeClass('not-in-view');
el.addClass('in-view');
} else {
e.preventDefault();
e.stopPropagation();
el.removeClass('in-view');
el.addClass('not-in-view');
}
scope.$apply();
});
}
};
})

Related

AngularJS: Cursor is moving to last position when model is transformed

I am using zipcode directive which is accepting 9 digit US zip code and formatting it.
app.directive('formatZipCode', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function fromUser(text) {
if (text) {
var transformedInput = text.replace(/[^0-9]/g, '');
if (transformedInput.length > 9) {
transformedInput = transformedInput.slice(0, 9);
}
if (transformedInput.length > 5) {
transformedInput = transformedInput.slice(0, 5) + "-" + transformedInput.slice(5);
}
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
});
when I am removing any number from zipcode, cursor position moves back to last position.
When I delete third number cursor moves to last number
How to retain the cursor position?
Your best option is to use ng-model-options to change when the input updates (and thus runs your formatter).
This is a case where you will sacrifice live reformatting in favor of form usability.
Either use
<input type="text" ng-model="zipcode" ng-model-options="{ updateOn: 'blur' }" format-zip-code />
to update when the user leaves the field, or
<input type="text" ng-model="zipcode" ng-model-options="{ debounce: 1000 }" format-zip-code />
to update after 1s. This option will still show the cursor moving to the end of the input after the update, but not instantly.
I have resolved this by using caret position. Remember the caret position and after formatting input assign the caret position again.
app.directive('formatZipCode', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function fromUser(text) {
if (text) {
var transformedInput = text.replace(/[^0-9]/g, '');
if (transformedInput.length > 9) {
transformedInput = transformedInput.slice(0, 9);
}
var caretPosition = element[0].selectionStart || undefined;
if (transformedInput.length > 5) {
var realposition = caretPosition == 6 ? caretPosition + 1 : caretPosition;
transformedInput = transformedInput.slice(0, 5) + "-" + transformedInput.slice(5);
}
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
if (typeof caretPosition === 'number') {
element[0].selectionStart = element[0].selectionEnd = realposition || caretPosition;
}
else {
element[0].selectionStart = element[0].selectionEnd = 0;
}
}
return transformedInput;
}
return undefined;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
});

Angular ui-bootstrap typeahead suggestion - scroll

According to this link :
up/down arrow key issue with typeahead control (angular bootstrap UI)
i have added these line in my js:
.directive('shouldFocus', function(){
return {
restrict: 'A',
link: function(scope,element,attrs){
scope.$watch(attrs.shouldFocus,function(newVal,oldVal){
element[0].scrollIntoView(false);
});
}
};
})
while scrolling, we get some more disturb, scroll was not smoothy.
Before adding this code, scroll was normal and smoothy.
Please anyone help me?
Hi here is another code only for keyup event to adjust the scroll height while up/down keypress
you need to add a function only to keyup in the typeahead directive in angular ui
search directive('typeahead' in the angular ui js file
find fireRecalculating function before it paste the following function
function makeSureVisible(){
$timeout(function(){
$el = popUpEl.find('.active');
if($el.length>0)
{
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.position().top;
console.log(elTop);
elBottom = elTop + $el.outerHeight(true);
nodeScrollTop = popUpEl.scrollTop();
nodeHeight = popUpEl.height() + parseInt(popUpEl.css("paddingTop"), 10) + parseInt(popUpEl.css("paddingBottom"), 10);
if (elTop < 0) {
popUpEl.scrollTop(nodeScrollTop + elTop);
} else if (nodeHeight < elBottom) {
popUpEl.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
}
}
});
}
now find keyup function attached. call the above function
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;
}
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
resetMatches();
scope.$digest();
return;
}
evt.preventDefault();
if (evt.which === 40) {
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
makeSureVisible();
} else if (evt.which === 38) {
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
makeSureVisible();
} 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();
}
});
function makeSureVisible() {
$timeout(function () {
$el = popUpEl[0].querySelector('.active');
if ($el) {
var elTop, elBottom, nodeScrollTop, nodeHeight;
elTop = $el.offsetTop;
elBottom = elTop + $el.offsetHeight;
nodeScrollTop = popUpEl[0].scrollTop;
nodeHeight = popUpEl[0].offsetHeight - (parseInt(window.getComputedStyle(popUpEl[0], null).getPropertyValue('padding-top')) + parseInt(window.getComputedStyle(popUpEl[0], null).getPropertyValue('padding-bottom')));
if (elTop < nodeScrollTop) {
popUpEl[0].scrollTop = elTop;
} else if (nodeHeight < elBottom) {
popUpEl[0].scrollTop = nodeScrollTop + elBottom - nodeHeight;
}
}
});
}
I bumped into the same issue with latest version of angular bootstrap 0.14.3 as of 27/11/2015.
Comments:
I placed the function in the element.bind("keydown") since it didn't work where you suggested. (function wasn't in proper scope and was undefined)
The first "if" wasn't triggered for me so i changed the logic a bit. When user reaches the end the dropdown scrolls to top properly in my case.
Modified it to work for plain javascript.
Thank you for your solution!
I had previously resolved this issue using the "shouldFocus" directive but I had to make more tweaks to get this to work. Perhaps this version will work for you.
.directive('shouldFocus', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(attrs.shouldFocus, function (newVal, oldVal) {
if (newVal && element.prop("class").indexOf("active")) {
var par = element.parent("ul");
var cellHeight = element.children().innerHeight();
var maxHeight = par.height();
var startIndex = Math.floor(maxHeight / cellHeight);
if (scope.$index > startIndex) {
var scroll = (scope.$index - startIndex) * cellHeight;
par.scrollTop(scroll);
}
if (scope.$index === 0) {
par.scrollTop(0);
}
}
});
}
}
})
Here is the modified template for those who don't know where to add the directive:
angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/typeahead/typeahead-popup.html",
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
" <li ng-repeat=\"match in matches track by $index\" should-focus=\"isActive($index)\" ng-class=\"{active: isActive($index) }\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{::match.id}}\">\n" +
" <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
" </li>\n" +
"</ul>\n" +
"");
}]);

angularjs : how to restrict input type number to allow only even number with min and max limit as well as steps to increase

I am working on one requirement where I want to allow only even numbers to text box or number box(input type number). with minimum and maximum limit like from 4 to 14 and it should only increase by step of 2 if we have number box.
I tried with HTML input type number with min max and step attributes it's working fine but we can edit the text box with any number so to restrict I tried using directive but it's not working out for me. I will be glad if anyone can help me out with this.
HTML :
<body ng-controller="ctrl">
new : <number-only-input step="2" min="4" max="14" input-value="wks.number" input-name="wks.name" >
</body>
Script :
var app = angular.module('app', []);
app.controller('ctrl', function($scope){
$scope.name = 'Samir Shah';
$scope.price = -10;
$scope.wks = {number: '', name: 'testing'};
});
app.directive('numberOnlyInput', function () {
return {
restrict: 'EA',
template: '<input type="text" name="{{inputName}}" ng-model="inputValue" />',
scope: {
inputValue: '=',
inputName: '=',
min: '#',
max: '#',
step: '#'
},
link: function (scope) {
scope.$watch('inputValue', function(newValue,oldValue) {
var arr = String(newValue).split("");
if (arr.length === 0) return;
if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.' )) return;
if (arr.length === 2 && newValue === '-.') return;
if (isNaN(newValue)) {
scope.inputValue = oldValue;
return;
}
if(!isNaN(newValue)){
if(newValue < parseInt(scope.min) || newValue > parseInt(scope.max)){
scope.inputValue = oldValue;
return;
}
}
});
}
};
});
<form name="testForm">
<div ng-controller="MyCtrl">
<input type="text" name="testInput" ng-model="number" ng-min="2" ng-max="14" required="required" numbers-only="numbers-only" />
<div ng-show="testForm.testInput.$error.nonnumeric" style="color: red;">
Numeric input only.
</div>
<div ng-show="testForm.testInput.$error.belowminimum" style="color: red;">
Number is too small.
</div>
<div ng-show="testForm.testInput.$error.abovemaximum" style="color: red;">
Number is too big.
</div>
<div ng-show="testForm.testInput.$error.odd" style="color: red;">
Numeric is odd.
</div>
</div>
</form>
angular.module('myApp', []).directive('numbersOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
element.bind('blur', function () {
if (parseInt(element.val(), 10) < attrs.ngMin) {
modelCtrl.$setValidity('belowminimum', false);
scope.$apply(function () {
element.val('');
});
}
});
modelCtrl.$parsers.push(function (inputValue) {
// this next if is necessary for when using ng-required on your input.
// In such cases, when a letter is typed first, this parser will be called
// again, and the 2nd time, the value will be undefined
if (inputValue == undefined) return ''
var transformedInput = inputValue.replace(/[^0-9]/g, '');
if (transformedInput != inputValue || (parseInt(transformedInput, 10) < parseInt(attrs.ngMin, 10) && transformedInput !== '1') || parseInt(transformedInput, 10) > parseInt(attrs.ngMax, 10) || (transformedInput % 2 !== 0 && transformedInput !== '1')) {
if (transformedInput != inputValue) {
modelCtrl.$setValidity('nonnumeric', false);
} else {
modelCtrl.$setValidity('nonnumeric', true);
}
if (parseInt(transformedInput, 10) < parseInt(attrs.ngMin, 10) && transformedInput !== '1') {
modelCtrl.$setValidity('belowminimum', false);
} else {
modelCtrl.$setValidity('belowminimum', true);
}
if (parseInt(transformedInput, 10) > parseInt(attrs.ngMax, 10)) {
modelCtrl.$setValidity('abovemaximum', false);
} else {
modelCtrl.$setValidity('abovemaximum', true);
}
if (transformedInput % 2 !== 0 && transformedInput !== '1') {
modelCtrl.$setValidity('odd', false);
} else {
modelCtrl.$setValidity('odd', true);
}
transformedInput = '';
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
return transformedInput;
}
modelCtrl.$setValidity('nonnumeric', true);
modelCtrl.$setValidity('belowminimum', true);
modelCtrl.$setValidity('abovemaximum', true);
modelCtrl.$setValidity('odd', true);
return transformedInput;
});
}
};
});
Active fiddle http://jsfiddle.net/tuckerjt07/1Ldmkmog/
You could define a property with getter and setter to process the entered value. If the value does not match the requrements display messages but not accept new value.
Using this method you could apply any validation logic, the second field editValue is needed because otherwise you could not enter an invalid number. Therefore editValue alows to enter numbers with numerous digits which will be partially invalid during entering the value.
Property:
// Property used to bind input containing validation
Object.defineProperty($scope, "number", {
get: function() {
return $scope.editValue;
},
set: function(value) {
value = parseInt(value);
$scope.editValue = value;
var isValid = true;
// Min?
if (value < parseInt($scope.min)) {
$scope.toSmall = true;
isValid = false;
} else {
$scope.toSmall = false;
}
// Max?
if (value > parseInt($scope.max)) {
$scope.toBig = true;
isValid = false;
} else {
$scope.toBig = false;
}
// Step not valid
if (value % parseInt($scope.step) > 0) {
$scope.stepNotValid = true;
isValid = false;
} else {
$scope.stepNotValid = false;
}
$scope.isValid = isValid;
if (isValid) {
$scope.value = value;
}
}
});
Working example
Below you can find a complete working example directive containing the property described above including increase/decrease buttons:
var app = angular.module('myApp', []);
app.directive('numberOnlyInput', function() {
return {
restrict: 'E',
template: '<input type="text" ng-model="number" ng-class="{\'error\': !isValid}"/><button ng-click="increase()">+</button><button ng-click="decrease()">-</button> Value: {{value}} {{stepNotValid ? (" value must be in steps of " + step) : ""}} {{toSmall ? " value must be greater or equal to " + min : ""}} {{toBig ? " value must be smaler or equal to " + max : ""}}',
scope: {
value: '=value',
min: '#',
max: '#',
step: '#'
},
link: function($scope) {
// Increase value
$scope.increase = function() {
var newValue = parseInt($scope.value) + parseInt($scope.step);
if (newValue <= $scope.max) {
$scope.number = newValue;
$scope.editValue = $scope.number;
}
};
// Decrease value
$scope.decrease = function() {
var newValue = parseInt($scope.value) - parseInt($scope.step);
if (newValue >= $scope.min) {
$scope.number = newValue;
$scope.editValue = $scope.number;
}
};
// Property used to bind input containing validation
Object.defineProperty($scope, "number", {
get: function() {
return $scope.editValue;
},
set: function(value) {
value = parseInt(value);
$scope.editValue = value;
var isValid = true;
// Min?
if (value < parseInt($scope.min)) {
$scope.toSmall = true;
isValid = false;
} else {
$scope.toSmall = false;
}
// Max?
if (value > parseInt($scope.max)) {
$scope.toBig = true;
isValid = false;
} else {
$scope.toBig = false;
}
// Step not valid
if (value % parseInt($scope.step) > 0) {
$scope.stepNotValid = true;
isValid = false;
} else {
$scope.stepNotValid = false;
}
$scope.isValid = isValid;
if (isValid) {
$scope.value = value;
}
}
});
// Init actual Value of the input element
$scope.number = parseInt($scope.value);
$scope.editValue = parseInt($scope.value);
}
};
});
app.controller('controller', function($scope) {
$scope.value = 10;
});
.error {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="controller">
Number:
<number-only-input min="4" max="14" step="2" value="value"></number-only-input>
</div>
Why are you doing too much of work of a simple thing. Max length will not work with <input type="number" the best way I know is to use oninput event to limit the maxlength. Please see the below code, Its a generic solution work with all the Javascript framework.
<input name="somename"
oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);"
type = "number"
maxlength = "6"
/>

Why is my custom signature pad directive not showing canvas? (without jQuery)

Hi I'm working on a signature pad for angularjs without importing jquery, i'm just using elements of jquery lite that angular uses. I have hit a wall and I have no idea how to get past it. Here's my code:
var sig = angular.module('signature', []);
sig.directive("signatureDirective", function () {
return {
template: '<canvas id="canvas" width="500" height="100" style="border: 1px solid #ccc;"></canvas>',
restrict: 'E',
link: function (scope, element, attrs) {
var canvas = $(element);
var context = canvas.getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
canvas.on("mousedown", mouseDown, false);
canvas.on("mousemove", mouseXY, false);
document.body.on("mouseup", mouseUp, false);
canvas.on("touchstart", mouseDown, false);
canvas.on("touchmove", mouseXY, true);
canvas.on("touchend", mouseUp, false);
canvas.on('touchmove', function (e) {
pen.x = e.pageX;
pen.y = e.pageY;
});
canvas.on('touchstart', function (e) {
context.fillRect(pen.x, pen.y, 1, 1);
});
canvas.on('mousemove', function (e) {
pen.x = e.pageX;
pen.y = e.pageY;
});
canvas.on('mousedown', function (e) {
context.fillRect(pen.x, pen.y, 1, 1);
});
context.getImageData();
document.body.on("touchcancel", mouseUp, false);
function draw() {
context.clearRect(0, 0, 500, 100);
context.strokeStyle = "#000000";
context.lineJoin = "miter";
context.lineWidth = 2;
for (var i = 0; i < clickX.length; i++) {
context.beginPath();
if (clickDrag[i] && i) {
context.moveTo(clickX[i - 1], clickY[i - 1]);
} else {
context.moveTo(clickX[i] - 1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.stroke();
context.closePath();
}
}
function mouseDown(e) {
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
draw();
}
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function mouseUp() {
paint = false;
}
function mouseXY(e) {
if (paint) {
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
draw();
}
}
}
};
});
I append it as an element but it doesnt show and javascript doesn't throw any exceptions. Can anyone help?
I found a great answer for you on stackoverflow, a canvas drawing directive with live example.
example: http://plnkr.co/aG4paH
HTML:
<div class="col-md-8" cg-signature>
<div class="sig sigWrapper">
<div class="typed"></div>
<canvas class="pad" width="197" height="52"></canvas>
<input type="hidden" name="output" class="output">
<a class="clearButton" href="#">Clear</a>
</div>
</div>
JavaScript:
angular.module('YourModuleName')
.directive('directiveName', function() {
var directive = {};
directive.restrict = 'A';
directive.link = function($scope, $ele, $attrs){
$ele.addClass('sigPad')
$ele.ready(function() {
$ele.signaturePad({drawOnly:true});
});
};
return directive;
});

angularjs move focus to next control on enter

What is the best way, when hitting enter inside a form, the focus to go to the next input instead submitting the form with angularjs.
I have a form with a lot of fields and customers are used to hit enter to move to the next input (comming from desktop applications). The angularjs saves the form when the user hits enter. I like to change this. Is it possible ?
I suggest making a custom directive. Something like this. I haven't tested this.
.directive('focus', function() {
return {
restrict: 'A',
link: function($scope,elem,attrs) {
elem.bind('keydown', function(e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
elem.next().focus();
}
});
}
}
});
Something like that should work. You might have to tweek something. Good luck.
Create a custom directive:
.directive('nextOnEnter', function () {
return {
restrict: 'A',
link: function ($scope, selem, attrs) {
selem.bind('keydown', function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
var pageElems = document.querySelectorAll('input, select, textarea'),
elem = e.srcElement || e.target,
focusNext = false,
len = pageElems.length;
for (var i = 0; i < len; i++) {
var pe = pageElems[i];
if (focusNext) {
if (pe.style.display !== 'none') {
angular.element(pe).focus();
break;
}
} else if (pe === elem) {
focusNext = true;
}
}
}
});
}
}
})
This is the directive I ended up with (thanks to Zack Argyle):
angular.module('myApp').directive("nextFocus", nextFocus);
/** Usage:
<input next-focus id="field1">
<input next-focus id="field2">
<input id="field3">
Upon pressing ENTER key the directive will switch focus to
the next field id e.g field2
The last field should not have next-focus directive to avoid
focusing on non-existing element.
Works for Web, iOS (Go button) & Android (Next button) browsers,
**/
function nextFocus() {
var directive = {
restrict: 'A',
link: function(scope, elem, attrs) {
elem.bind('keydown', function(e) {
var partsId = attrs.id.match(/field(\d{1})/);
var currentId = parseInt(partsId[1]);
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
document.querySelector('#field' + (currentId + 1)).focus();
}
});
}
};
return directive;
}
I tried this solution out. As advertised, it needed some tweaking. Here is what ended up working for me:
.directive("focus", function () {
return {
restrict: "A",
link: function ($scope, elem, attrs) {
var focusables = $(":focusable");
elem.bind("keydown", function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
var current = focusables.index(this);
var next = focusables.eq(current + 1).length ? focusables.eq(current + 1) : focusables.eq(0);
next.focus();
e.preventDefault();
}
});
}
}
Note that the in order to get the :focusable pseudo to work, you will need to reference JQueryUI. (the latest version 1.11.4 worked for me)
This is the directive I ended up with (thanks to Zack Argyle and Oleg):
app.directive("nextFocus", function () {
/** Usage:
<input next-focus tabindex="0" id="field1">
<input next-focus tabindex="1" id="field2">
<input id="field3">
Upon pressing ENTER key the directive will switch focus to
the next field id e.g field2
The last field should not have next-focus directive to avoid
focusing on non-existing element.
Works for Web, iOS (Go button) & Android (Next button) browsers,
**/
var directive = {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('keydown', function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
try {
if (attrs.tabindex != undefined) {
var currentTabIndex = attrs.tabindex;
var nextTabIndex = parseInt(attrs.tabindex) + 1;
$("[tabindex=" + nextTabIndex + "]").focus();
}
} catch (e) {
}
}
});
}
};
return directive;
});
Based on the answer by wolcy97 but using only angular
/** Usage:
<input next-focus tabindex="0">
<input next-focus tabindex="1">
<input tabindex="2">
Upon pressing ENTER key the directive will switch focus to
the next tabindex.
The last field should not have next-focus directive to avoid
focusing on non-existing element.
Works for Web, iOS (Go button) & Android (Next button) browsers,
**/
app.directive('nextFocus', [function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
elem.bind('keydown', function(e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
try {
if (attrs.tabindex !== undefined) {
var currentTabeIndex = attrs.tabindex;
var nextTabIndex = parseInt(currentTabeIndex) + 1;
var elems = document.querySelectorAll("[tabindex]");
for (var i = 0, len = elems.length; i < len; i++) {
var el = angular.element(elems[i]);
var idx = parseInt(el.attr('tabindex'));
if (idx === nextTabIndex) {
elems[i].focus();
break;
}
}
}
} catch (e) {
console.log('Focus error: ' + e);
}
}
});
}
};
}]);
Pure JavaScript Enter as TAB
angular.module('app').directive('tabNext', function () {
return {
restrict: 'A',
link: function (scope, elem) {
elem.bind('keyup', function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
var eIDX = -1;
for (var i = 0; i < this.form.elements.length; i++) {
if (elem.eq(this.form.elements[i])) {
eIDX = i;
break;
}
}
if (eIDX === -1) {
return;
}
var j = eIDX + 1;
var theform = this.form;
while (j !== eIDX) {
if (j >= theform.elements.length){
j = 0;
}
if ((theform.elements[j].type !== "hidden") && (theform.elements[j].type !== "file")
&& (theform.elements[j].name !== theform.elements[eIDX].name)
&& (! theform.elements[j].disabled)
&& (theform.elements[j].tabIndex >= 0)) {
if (theform.elements[j].type === "select-one") {
theform.elements[j].focus();
} else if (theform.elements[j].type === "button") {
theform.elements[j].focus();
} else {
theform.elements[j].focus();
theform.elements[j].select();
}
return;
break;
}
j++;
}
}
});
}
}});
<table class="table table-striped table-bordered table-hover">
<tr>
<th>S No</th>
<th>Stock Id</th>
<th>Description</th>
<th>Qty</th>
<th>UOM</th>
<th>Rate</th>
<th>Amount</th>
<th>Add</th>
<th>Delete</th>
</tr>
<tr ng-repeat="item in stockitems">
<td>{{$index + 1}}</td>
<td>
<input type="text" style="width:70px" id="stkid{{$index}}" class="form-control" name="stockid" required insert="Addnewrow();" ng-keyup="moveFocus('desc','amount','stkid','stkid',$index,$event)" ng-blur="getStockitem($index);" typeahead="a.stockitem_code as (a.stockitem_code +' | ' + a.stockitem_name +' | '+ a.rate) for a in stock | filter:$viewValue | limitTo:8" data-ng-model="item.stockid" rows="3" />
</td>
<td>
<input type="text" class="form-control" id="desc{{$index}}" name="description" ng-keyup="moveFocus('quantity','stkid','desc','desc',$index,$event)" data-ng-model="item.description" rows="3" />
</td>
<td>
<input type="text" style="width:70px" id="quantity{{$index}}" class="form-control" ng-keyup="moveFocus('uom','desc','quantity','quantity',$index,$event)" ng-change="GetAmount($index,'qty');" ng-pattern="/^\d+$/" required name="qty" data-ng-model="item.qty" rows="3" />
</td>
<td>
<input type="text" style="width:70px" id="uom{{$index}}" class="form-control" name="uom" ng-keyup="moveFocus('rate','quantity','uom','uom',$index,$event)" data-ng-model="item.uom" required rows="3" />
</td>
<td>
<input type="text" style="width:70px" id="rate{{$index}}" class="form-control" name="rate" ng-keyup="moveFocus('amount','uom','rate','rate',$index,$event)" required data-ng-model="item.rate" ng-pattern="/^\d{0,9}(\.\d{1,9})?$/" ng-change="GetAmount($index,'rate');" rows="3" />
</td>
<td>
<input type="text" style="width:70px" id="amount{{$index}}" class="form-control" ng-keyup="moveFocus('stkid','rate','amount','amount',$index,$event)" name="amount" required data-ng-model="item.amount" rows="3" />
</td>
<td><span ng-click="AddEstimation($index);"><a>Add</a></span></td>
<td><span ng-click="DeleterowEstimation($index);"><a>Delete</a></span></td>
</tr>
</table>
$scope.moveFocus = function (nextId,prevId,downId,upId,index,event) {
debugger;
if (event.keyCode == 39) {
nextId = nextId + index;
$('#' + nextId).focus();
}
else if(event.keyCode == 37)
{
prevId = prevId + index;
$('#' + prevId).focus();
}
else if(event.keyCode == 38)
{
upId = upId + (index - 1);
$('#' + upId).focus();
}
else if(event.keyCode == 40)
{
downId = downId + (index + 1);
$('#' + downId).focus();
}
else if(event.keyCode==13)
{
if (nextId == "desc") {
nextId = "quantity" + index;
$('#' + nextId).focus();
}
else if(nextId == "uom")
{
nextId = "stkid" + (index + 1);
$('#' + nextId).focus();
}
}
};
On Enter press it moves to next element of DOM, but element requires id to set focus
starter.directive('focustonext', function () {
return {
restrict: 'A',
link: function ($scope, selem, attrs) {
selem.bind('keydown', function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
var pageElems = document.querySelectorAll('input, select, textarea'),
elem = e.srcElement || e.target,
focusNext = false,
len = pageElems.length;
for (var i = 0; i < len; i++) {
var pe = pageElems[i];
if (focusNext) {
if (pe.style.display !== 'none') {
document.getElementById(pe.id).focus();
break;
}
} else if (pe === elem) {
focusNext = true;
}
}
}
});
}
}
});
Thanks all..

Resources