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..
Related
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);
}
};
});
I'm developing a upvote/downvote controlling system for a dynamic bunch of cards.
I can controll if I click to the img the checked = true and checked = false value but The problem I've found and because my code doesn't work as expected is I can't update my value in the ng-model, so the next time the function is called I receive the same value. As well, I can't update and show correctly the new value. As well, the only card that works is the first one (it's not dynamic)
All in which I've been working can be found in this plunk.
As a very new angular guy, I tried to investigate and read as much as possible but I'm not even 100% sure this is the right way, so I'm totally open for other ways of doing this, attending to performance and clean code. Here bellow I paste what I've actually achieved:
index.html
<card-interactions class="card-element" ng-repeat="element in myIndex.feed">
<label class="rating-upvote">
<input type="checkbox" ng-click="rate('upvote', u[element.id])" ng-true-value="1" ng-false-value="0" ng-model="u[element.id]" ng-init="element.user_actions.voted === 'upvoted' ? u[element.id] = 1 : u[element.id] = 0" />
<ng-include src="'upvote.svg'"></ng-include>
{{ element.upvotes + u[1] }}
</label>
<label class="rating-downvote">
<input type="checkbox" ng-click="rate('downvote', d[element.id])" ng-model="d[element.id]" ng-true-value="1" ng-false-value="0" ng-init="element.user_actions.voted === 'downvoted' ? d[element.id] = 1 : d[element.id] = 0" />
<ng-include src="'downvote.svg'"></ng-include>
{{ element.downvotes + d[1] }}
</label>
<hr>
</card-interactions>
index.js
'use strict';
var index = angular.module('app.index', ['index.factory']);
index.controller('indexController', ['indexFactory', function (indexFactory) {
var data = this;
data.functions = {
getFeed: function () {
indexFactory.getJSON(function (response) {
data.feed = response.index;
});
}
};
this.functions.getFeed();
}
]);
index.directive('cardInteractions', [ function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
scope.rate = function(action, value) {
var check_up = element.find('input')[0];
var check_down = element.find('input')[1];
if (action === 'upvote') {
if (check_down.checked === true) {
check_down.checked = false;
}
} else {
if (action === 'downvote') {
if (check_up.checked === true) {
check_up.checked = false;
}
}
}
}
}
};
}]);
Hope you guys can help me with this.
Every contribution is appreciated.
Thanks in advice.
I have updated your directive in this plunker,
https://plnkr.co/edit/HvcBv8XavnDZTlTeFntv?p=preview
index.directive('cardInteractions', [ function () {
return {
restrict: 'E',
scope: {
vote: '='
},
templateUrl: 'vote.html',
link: function (scope, element, attrs) {
scope.vote.upValue = scope.vote.downValue = 0;
if(scope.vote.user_actions.voted) {
switch(scope.vote.user_actions.voted) {
case 'upvoted':
scope.vote.upValue = 1;
break;
case 'downvoted':
scope.vote.downValue = 1;
break;
}
}
scope.upVote = function() {
if(scope.vote.downValue === 1) {
scope.vote.downValue = 0;
scope.vote.downvotes--;
}
if(scope.vote.upValue === 1) {
scope.vote.upvotes++;
} else {
scope.vote.upvotes--;
}
};
scope.downVote = function() {
if(scope.vote.upValue === 1) {
scope.vote.upValue = 0;
scope.vote.upvotes--;
}
if(scope.vote.downValue === 1) {
scope.vote.downvotes++;
} else {
scope.vote.downvotes--;
}
};
}
};
I have a field which takes inputs of time something like 12:00 / 24:00
I want used to be able tot fill only digits and : in the field and on change validate it with proper time. I want to do it in angular way. How do i do it.
Here is the code i have written. This can be done in 1 directive also. This code works, But not better code.
<input type="text" ng-model-options="{allowInvalid: true}" ng-blur="validateHhMm(this)" ng-model='time' time-only/>
angular.module('abc').directive('timeOnly', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue ? inputValue.replace(/[^\d:]/g,'') : null;
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
$scope.validateHhMm = function (inputField)
{
var errorMsg = "";
var regs = '';
// regular expression to match required time format
var re = /^(\d{1,2}):(\d{2})?$/;
var inputvalue = $scope.time;
if(inputvalue != '')
{
if(regs = inputvalue.match(re))
{
if(regs[4])
{
// 12-hour time format with am/pm
if(regs[1] < 1 || regs[1] > 12)
{
errorMsg = "Invalid value for hours: " + regs[1];
}
}
else
{
if(regs[1] > 23)
{
errorMsg = "Invalid value for hours: " + regs[1];
}
}
if(!errorMsg && regs[2] > 59)
{
errorMsg = "Invalid value for minutes: " + regs[2];
}
}
else
{
errorMsg = "Invalid time format: " + inputvalue;
}
}
if(errorMsg != "")
{
//console.log(errorMsg);
//field.focus();
$scope.time = '';
return false;
}
return true;
};
try this way
<input type="time" id="exampleInput" name="input" ng-model="example.value"
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
<span class="error" ng-show="myForm.input.$error.time">
Not a valid date!</span>
</div>
Here is the plunker
This is the example from here
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"
/>
I am trying to use this Sticky CSS plugin with an Angular Directive. I tried wrapping this code into a Directive but no luck yet getting it to work.
Here is the CodePen of the plugin without Angular - http://codepen.io/chrissp26/pen/gBrdo
and this is what I have so far.
Any help or guidance would be greatly appreciated.
app.directive('sticky', function() {
return function stickyTitles(stickies) {
this.load = function() {
stickies.each(function(){
var thisSticky = jQuery(this).wrap('<div class="followWrap" />');
thisSticky.parent().height(thisSticky.outerHeight());
jQuery.data(thisSticky[0], 'pos', thisSticky.offset().top);
});
}
this.scroll = function() {
stickies.each(function(i){
var thisSticky = jQuery(this),
nextSticky = stickies.eq(i+1),
prevSticky = stickies.eq(i-1),
pos = jQuery.data(thisSticky[0], 'pos');
if (pos <= jQuery(window).scrollTop()) {
thisSticky.addClass("fixed");
if (nextSticky.length > 0 && thisSticky.offset().top >= jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight()) {
thisSticky.addClass("absolute").css("top", jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight());
}
} else {
thisSticky.removeClass("fixed");
if (prevSticky.length > 0 && jQuery(window).scrollTop() <= jQuery.data(thisSticky[0], 'pos') - prevSticky.outerHeight()) {
prevSticky.removeClass("absolute").removeAttr("style");
}
}
});
}
}
return function(){
var newStickies = new stickyTitles(jQuery(".followMeBar"));
newStickies.load();
jQuery(window).on("scroll", function() {
newStickies.scroll();
});
};
});
try this:
<sticky>
<div class="followMeBar">a</div>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<div class="followMeBar">b</div>
</sticky>
directive:
app.directive('sticky', function() {
var stickyTitles = function (stickies) {
this.load = function() {
stickies.each(function() {
var thisSticky = jQuery(this).wrap('<div class="followWrap" />');
thisSticky.parent().height(thisSticky.outerHeight());
jQuery.data(thisSticky[0], 'pos', thisSticky.offset().top);
});
}
this.scroll = function() {
stickies.each(function(i) {
var thisSticky = jQuery(this),
nextSticky = stickies.eq(i + 1),
prevSticky = stickies.eq(i - 1),
pos = jQuery.data(thisSticky[0], 'pos');
if (pos <= jQuery(window).scrollTop()) {
thisSticky.addClass("fixed");
if (nextSticky.length > 0 && thisSticky.offset().top >= jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight()) {
thisSticky.addClass("absolute").css("top", jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight());
}
} else {
thisSticky.removeClass("fixed");
if (prevSticky.length > 0 && jQuery(window).scrollTop() <= jQuery.data(thisSticky[0], 'pos') - prevSticky.outerHeight()) {
prevSticky.removeClass("absolute").removeAttr("style");
}
}
});
}
}
return {
restrict: 'E',
link: function(scope, element, attrs) {
var newStickies = new stickyTitles($(element).find(".followMeBar"));
newStickies.load();
jQuery(window).on("scroll", function() {
newStickies.scroll();
});
}
};
http://plnkr.co/edit/13w5e7n0ReWoaO8513K5?p=preview