Directive defined to add numeric input
app.directive('numericCheck', function () {
return {
link: function (scope, element, attr) {
element.on('keypress', function (event) {
event = (event) ? event : window.event;
if(/Firefox/.test(window.navigator.userAgent) && event.keyCode!=0) {
return true;
}
var charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && charCode != 46 && charCode != 47 && charCode != 45 && (charCode < 48 || charCode > 57) || (charCode==45 && (event.target.selectionStart!=0 || event.target.value.indexOf('-')>=0)) || (charCode==46 && event.target.value.indexOf('.')>=0) || (charCode==47 && (event.target.selectionStart==0 || event.target.value.indexOf('/')>=0))) {
return false;
}
return true;
});
}
};
});
The below string is the response added to a div.
{"question":"Two identical charts have different parts shaded. Which one is greater - 0.02 or 0.04?<br><br><br><br><input type='text' id='b1' name='blank_1' class='num_blank' size='5' numeric-check> is the greater number."}
After adding the response to the HTML numeric-check, directive does not work and it allows us to enter all characters instead of only numeric values.
Have you tried to put something like:
app.directive('numericCheck', function () {
return {
link: function (scope, element, attr) {
element.on('keypress', function (event) {
scope.$apply(function(){
event = (event) ? event : window.event;
if(/Firefox/.test(window.navigator.userAgent) && event.keyCode!=0) {
return true;
}
var charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && charCode != 46 && charCode != 47 && charCode != 45 && (charCode < 48 || charCode > 57) || (charCode==45 && (event.target.selectionStart!=0 || event.target.value.indexOf('-')>=0)) || (charCode==46 && event.target.value.indexOf('.')>=0) || (charCode==47 && (event.target.selectionStart==0 || event.target.value.indexOf('/')>=0))) {
return false;
}
return true;
});
});
}
};
Related
I am working on migration of angular 1 project to angular 2 . In angular 1 project I was using angular.equals for object comparison angular.equals($ctrl.obj1, $ctrl.newObj); , I searched online for equivalent method in angular 2 but could not find any matching result.
#Günter Yes you are right there is no equivalent in angular2 . While searching more I found third party library lodash which will do same job as angular.equals and syntax is same as angular one and this library solves my problem
Code example from lodash documentation
var object = { 'a': 1 };
var other = { 'a': 1 };
_.isEqual(object, other);
// => true
object === other;
// => false
I rewrote Ariels answer (thank you!) to be TSLINT-friendly. You can also save some continues by using else if, but I think this is more clear. Maybe someone else needs it too:
export function deepEquals(x, y) {
if (x === y) {
return true; // if both x and y are null or undefined and exactly the same
} else if (!(x instanceof Object) || !(y instanceof Object)) {
return false; // if they are not strictly equal, they both need to be Objects
} else if (x.constructor !== y.constructor) {
// they must have the exact same prototype chain, the closest we can do is
// test their constructor.
return false;
} else {
for (const p in x) {
if (!x.hasOwnProperty(p)) {
continue; // other properties were tested using x.constructor === y.constructor
}
if (!y.hasOwnProperty(p)) {
return false; // allows to compare x[ p ] and y[ p ] when set to undefined
}
if (x[p] === y[p]) {
continue; // if they have the same strict value or identity then they are equal
}
if (typeof (x[p]) !== 'object') {
return false; // Numbers, Strings, Functions, Booleans must be strictly equal
}
if (!deepEquals(x[p], y[p])) {
return false;
}
}
for (const p in y) {
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
return false;
}
}
return true;
}
}
Instead of writing a function to iterate through the objects, you could just use JSON.stringify and compare the two strings?
Example:
var obj1 = {
title: 'title1',
tags: []
}
var obj2 = {
title: 'title1',
tags: ['r']
}
console.log(JSON.stringify(obj1));
console.log(JSON.stringify(obj2));
console.log(JSON.stringify(obj1) === JSON.stringify(obj2));
In Angular 2 you should use pure JavaScript/TypeScript for that so you can add this method to some service
private static equals(x, y) {
if (x === y)
return true;
// if both x and y are null or undefined and exactly the same
if (!(x instanceof Object) || !(y instanceof Object))
return false;
// if they are not strictly equal, they both need to be Objects
if (x.constructor !== y.constructor)
return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
let p;
for (p in x) {
if (!x.hasOwnProperty(p))
continue;
// other properties were tested using x.constructor === y.constructor
if (!y.hasOwnProperty(p))
return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if (x[p] === y[p])
continue;
// if they have the same strict value or identity then they are equal
if (typeof (x[p]) !== "object")
return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if (!RXBox.equals(x[p], y[p]))
return false;
}
for (p in y) {
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p))
return false;
}
return true;
}
You could just copy the original source code from angularjs for the angular.equals function. Usage: equals(obj1, obj2);
var toString = Object.prototype.toString;
function isDefined(value) {return typeof value !== 'undefined';}
function isFunction(value) {return typeof value === 'function';}
function createMap() {
return Object.create(null);
}
function isWindow(obj) {
return obj && obj.window === obj;
}
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
function isRegExp(value) {
return toString.call(value) === '[object RegExp]';
}
function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
function isDate(value) {
return toString.call(value) === '[object Date]';
}
function isArray(arr) {
return Array.isArray(arr) || arr instanceof Array;
}
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
// eslint-disable-next-line no-self-compare
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
if (t1 === t2 && t1 === 'object') {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) === o2.length) {
for (key = 0; key < length; key++) {
if (!equals(o1[key], o2[key])) return false;
}
return true;
}
} else if (isDate(o1)) {
if (!isDate(o2)) return false;
return simpleCompare(o1.getTime(), o2.getTime());
} else if (isRegExp(o1)) {
if (!isRegExp(o2)) return false;
return o1.toString() === o2.toString();
} else {
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
keySet = createMap();
for (key in o1) {
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
if (!equals(o1[key], o2[key])) return false;
keySet[key] = true;
}
for (key in o2) {
if (!(key in keySet) &&
key.charAt(0) !== '$' &&
isDefined(o2[key]) &&
!isFunction(o2[key])) return false;
}
return true;
}
}
return false;
}
a = { name: 'me' }
b = { name: 'me' }
a == b // false
a === b // false
JSON.stringify(a) == JSON.stringify(b) // true
JSON.stringify(a) === JSON.stringify(b) // true
I am trying to implement mobile number validation on a Visualforce page using Angular JS, but am having problems getting my head around how to write the regex expression.
The requirements, as given to me, are fairly simple:
The number should be 10 digits long (I've already set the maxlength attribute on the field so this one is kind of taken care of already)
It should start with 04 (as it is an Australian mobile)
Only numbers should be allowed.
The regex expression I am using (in the ng-pattern attribute for my phone number input field) is:
^/(04)[0-9]{10}/$
This works up to a point - it does not allow anything other than numbers and does let me enter a 10 digit number starting with 04. However it also lets me enter numbers starting with, for example, 02, 03 etc....
Probably quite a simple thing I'm missing but I've had a look at quite a few sites, including this one, and can't find the answer.
Any help would be hugely appreciated as this one has caused me a few grey hairs already.
Try using this pattern
Use this in ur HTML file:
<input type="text" (keypress)="keyPress($event)" minlength=10 maxlength=10>
Use this in ur JS file:
keyPress(event: any) {
const pattern = /[0-9\+\-\ ]/;
let inputChar = String.fromCharCode(event.charCode);
if (event.keyCode != 8 && !pattern.test(inputChar)) {
event.preventDefault();
}
}
Try this one:
Mobile Number :
//inside view:
<input type="text" class="form-control" ng-model="mobileNo" name="mobileNo" ng-pattern="regEx" />
//inside controller:
$scope.regEx="/^[0-9]{10,10}$/";
For starters I had to create a whole js function for it... and it does validates as you type. here is my full code I hope this can help you get where you need.
This function gets the string every time a key is beign pressed and it allows the carrete to move front, back, delete and backspace. check it out and let me know if it helps you. you can run it on any situation and this is how I would add the "04" validation
//phone validation 10 digits and parenthesis (344)567-0011
function validatePhone(inputId) {
let validKey = false;
const input = document.getElementById(inputId);
let enteredDigits = input.value;
//switch to remove the country code added by default on autoComplete forms.
if (enteredDigits.length > 10 && enteredDigits[0] == '+') {
switch (enteredDigits.length) {
case 12:
enteredDigits = enteredDigits.slice(2);
break;
case 13:
enteredDigits = enteredDigits.slice(3);
break;
case 14:
enteredDigits = enteredDigits.slice(4);
break;
default:
enteredDigits = enteredDigits.replace(/\D+/g, '');
}
}
let newPhone = enteredDigits.replace(/\D+/g, ''); // This replace any character that is not a number.
const key = event.keyCode || event.charCode; // Get the pressed key.
let caretPosition = input.selectionStart; // get the carret position.
// splits, removes the "-" and converts from array to string and gives the needed digits.
const areaCode = newPhone.split('').splice(0, 3).toString().replace(/,/g, '');
const threeDigit = newPhone.split('').splice(3, 3).toString().replace(/,/g, '');
const fourDigit = newPhone.split('').splice(6, 8).toString().replace(/,/g, '');
// Moving carret on different positions. when the numeric keys are being pressed.
// Key >= 48 && key <= 58 number keys.
// Key >= 96 && key <= 105 numeric path number keys.
if ((key >= 48 && key <= 58) || (key >= 96 && key <= 105)) {
validKey = true;
if (threeDigit.length > 0) {
if (caretPosition == 1) {
caretPosition = caretPosition + 1;
} else if (caretPosition == 4 && newPhone.length == 4) {
caretPosition = caretPosition + 2;
} else if (caretPosition == 4 && newPhone.length >= 5) {
caretPosition = caretPosition + 1;
} else if (caretPosition == 5) {
caretPosition = caretPosition + 1;
} else if (caretPosition >= 2 && caretPosition <= 3 && newPhone.length <= 4) {
caretPosition = caretPosition + 1;
}
}
if (fourDigit.length > 0 && caretPosition == 9) {
caretPosition = caretPosition + 1;
}
}
// Key = 8 = Backspace.
else if (key == 8) {
validKey = true;
if (caretPosition == 3 && newPhone.length == 3) {
caretPosition = caretPosition - 1;
} else if (caretPosition == 2 && newPhone.length == 3) {
caretPosition = caretPosition - 1;
} else if (caretPosition == 1 && newPhone.length == 3 && threeDigit.length == 0) {
caretPosition = caretPosition - 1;
}
}
// Key = 46 = Delete. Key =37 = ArrowLeft. Key = 39 = ArrowRight.
else if (key == 46 || key == 39 || key == 37) {
validKey = true;
// Delete
if (key == 46) {
if (caretPosition == 0 && newPhone.length > 3) {
caretPosition = caretPosition + 1;
} else if (caretPosition == 1 && newPhone.length == 3) {
caretPosition = caretPosition - 1;
} else if (caretPosition == 2 && newPhone.length == 3) {
caretPosition = caretPosition - 1;
} else if (caretPosition == 3 && newPhone.length == 3) {
caretPosition = caretPosition - 1;
} else if ((newPhone.length >= 4 && caretPosition == 4) || (newPhone.length >= 4 && caretPosition == 8)) {
caretPosition = caretPosition + 1;
}
}
}
//here is the validation for the country that you need.
if ((newPhone.length == 1 && newPhone[0] != '0') || (newPhone.length >= 2 && newPhone[1] != '4')) {
alert('Must start with 04');
newPhone = '';
}
// Adding the special character for formatting.
if (threeDigit.length > 0 && fourDigit.length == 0) {
newPhone = '(' + areaCode + ')' + threeDigit;
} else if (fourDigit.length > 0 && threeDigit.length == 3) {
newPhone = '(' + areaCode + ')' + threeDigit + '-' + fourDigit;
}
if (!validKey) {
caretPosition = caretPosition - 1;
}
// Set new values.
newPhone = newPhone.substring(0, 13);
input.value = newPhone;
input.focus();
input.setSelectionRange(caretPosition, caretPosition);
}
<form name="myForm"
onsubmit="return validateForm()"
method="post">
Phone number: <input type="text"
id="phoneNumber"
name="fPhone"
onkeyup="validatePhone('phoneNumber')">
<input type="submit"
value="Submit">
</form>
I have a directive I have created to validate password strength.
/**
*
* A directive that compares the model value of the associated input
* against an algorythm to prevent poor passwords.
*
* Passwords must contain the following.
* at least one uppercase letter
* at least one lowercase letter
* at least one numeric character
* at least one special character
* No more than 2 sequential alpha characters
* No more than 2 sequential numeric characters
* No more than 2 consecutive alpha characters
* No consecutive numeric characters
* No consecutive symbols
*
*/
app.directive('passwordStrength',[function()
{
return {
restrict: "A",
require: 'ngModel',
link: function($scope, $element, $attrs, ctrl)
{
if(!ctrl) return;
// Observe when a change is made to the comparitor value
$attrs.$observe('passwordStrength', function(value)
{
ctrl.$validate();
});
//
ctrl.$validators.passwordStrength = function(modelValue, viewValue)
{
// Strings to use to validate against sequentials.
// and accepted special characters.
var sAlphas = "abcdefghijklmnopqrstuvwxyz";
var sNumerics = "0123456789";
var sSymbols = '?!##$%^&*(){}[]<>';
var sFwd = "";
var sRev = "";
var alphaFail = false;
var numericFail = false;
var alphaConsecFail = false;
var numericConsecFail = false;
var symbolConsecFail = false;
console.log($element.value);
// Fail if the password doesn't contain at leasto one uppercase letter
if(!modelValue.match(/[A-Z]/))
{
ctrl.$setValidity('alphaUpper', false);
console.log("Failed uppercase character check");
return false;
}
// Fail if the password doesn't contain at leasto one lowercase letter
if(modelValue.match(/[a-z]/))
{
ctrl.$setValidity('alphaLower', false);
console.log("Failed lowercase character check");
return false;
}
// Fail if the password doesn't contain at least one number
if(modelValue.match(/[0-9]/))
{
ctrl.$setValidity('numeric', false);
console.log("Failed numeric character check");
return false;
}
// Make sure that there are no sequential letters of 3 or more.
for (var s=0; s < 23; s++)
{
sFwd = sAlphas.substring(s,parseInt(s+3)).toLowerCase();
sRev = sFwd.strReverse();
if(
(modelValue.indexOf(sFwd) != -1 && modelValue != "") ||
(modelValue.indexOf(sRev) != -1 && modelValue != "")
)
{
alphaFail = true;
}
}
if(alphaFail)
{
console.log("Failed sequential alpha character check");
ctrl.$setValidity('serialAlpha', false);
return false;
}
// Make sure that there are no sequential letters of 3 or more.
var numericFail = false;
for (var s=0; s < 8; s++)
{
sFwd = sNumerics.substring(s,parseInt(s+3));
sRev = sFwd.strReverse();
if(
(modelValue.indexOf(sFwd) != -1 && modelValue != "") ||
(modelValue.indexOf(sRev) != -1 && modelValue != "")
)
{
numericFail = true;
}
}
if(numericFail)
{
console.log("Failed sequential numeric character check");
ctrl.$setValidity('serialNumeric', false);
return false;
}
// Look for consecutive letters
for (var s=0; s < 26; s++)
{
testCase = Array(4).join(sAlphas.substring(s,parseInt(s+1)));
if(
(modelValue.indexOf(testCase) != -1 && modelValue != "") ||
(modelValue.indexOf(testCase.toUpperCase()) != -1 && modelValue != "")
)
{
alphaConsecFail = true;
}
}
if(alphaConsecFail)
{
console.log("Failed consecutive alpha character check");
ctrl.$setValidity('consecutiveAlpha', false);
return false;
}
// Look for consecutive numbers
for (var s=0; s < 10; s++)
{
testCase = Array(4).join(sNumerics.substring(s,parseInt(s+1)));
if(modelValue.indexOf(testCase) != -1 && modelValue != "")
{
numericConsecFail = true;
}
}
if(numericConsecFail)
{
console.log("Failed consecutive number character check");
ctrl.$setValidity('consecutiveNum', false);
return false;
}
// Look for consecutive symbols
for (var s=0; s < 10; s++)
{
testCase = Array(3).join(sSymbols.substring(s,parseInt(s+1)));
if(modelValue.indexOf(testCase) != -1 && modelValue != "")
{
symbolConsecFail = true;
}
}
if(symbolConsecFail)
{
console.log("Failed consecutive symbol character check");
ctrl.$setValidity('consecutiveSym', false);
return false;
}
console.log("Succeeded vaidation");
ctrl.$setValidity('alphaUpper', true);
ctrl.$setValidity('alphaLower', true);
ctrl.$setValidity('numeric', true);
ctrl.$setValidity('serialAlpha', true);
ctrl.$setValidity('serialNumeric', true);
ctrl.$setValidity('consecutiveAlpha', true);
ctrl.$setValidity('consecutiveNum', true);
ctrl.$setValidity('consecutiveSym', true);
return true;
}
}
};
}]);
If I attach this to 2 different form fields inside the same HTML template for some reason instead of validating them separately like I expected it seems to validate both of them at the same time which basically breaks the validation. I don't understand what I need todo to get them to validate separately.
Thoughts?
Perhaps you could isolate the scope of the directive by setting scope: true in the object returned:
return {
restrict: "A",
scope: true,
require: 'ngModel',
link: function($scope, $element, $attrs, ctrl){
...
}
}
More info: angularjs directive scope default value?
While I already gave a correct answer to #jsonmurphy I found that the following also works.
return {
restrict: "A",
require: '^ngModel',
link: function($scope, $element, $attrs, ctrl){
...
}
}
I want to change value's based on other values in a repeat. I made a watch but the thing is that if i alter the newValue inside the watch the watch is triggered again.
Is there a way to solve this problem
my watch:
$scope.$watch('objlist', function(newValue, oldValue) {
$scope.counter += 1;
for (var count = 0; count < newValue.length; ++count) {
if (typeof newValue[count] != "undefined" && (typeof oldValue == "undefined" || newValue[count].value1 != oldValue[count].value1)) {
newValue[count].value3 = newValue[count].value1 * newValue[count].value2;
newValue[count].value3 = Number(newValue[count].value3).toFixed(2);
}
if (typeof newValue[count] != "undefined" && (typeof oldValue == "undefined" || newValue[count].value3 != oldValue[count].value3)) {
newValue[count].value1 = newValue[count].value3 / newValue[count].value2;
newValue[count].value1 = Number(newValue[count].value1).toFixed(2);
}
}
}, true);
i have included a plunker to show you what i mean.
plunker
as you can see if you change the value of field 1 or 3 it automaticly adds 2 to the counter and sets both to toFixed(2). This makes it hard to edit the fields.
We use angular 1.2.25
You could use a small trick with a condition.
For instance :
$scope.watchActivated = true;
$scope.$watch('objlist', function(newValue, oldValue) {
if ( $scope.watchActivated ) {
$scope.watchActivated = false;
$scope.counter += 1;
for (var count = 0; count < newValue.length; ++count) {
if (typeof newValue[count] != "undefined" && (typeof oldValue == "undefined" || newValue[count].value1 != oldValue[count].value1) )
{
newValue[count].value3 = newValue[count].value1 * newValue[count].value2;
newValue[count].value3 = Number(newValue[count].value3).toFixed(2);
}
if (typeof newValue[count] != "undefined" && (typeof oldValue == "undefined" || newValue[count].value3 != oldValue[count].value3) )
{
newValue[count].value1 = newValue[count].value3 / newValue[count].value2;
newValue[count].value1 = Number(newValue[count].value1).toFixed(2);
}
}
$scope.watchActivated = true;
}
}, true);
Far from elegant but it should work.
Careful the code might not be good I just did a quick edit.
I have created a directive, that creates a popup with a ul element, but ng-class = isVisible is not returning the correct value. It works, if I only have one use of the directive at my page, but when multiple, it does not work.
My problem is, that ngClass is not updated when using this scope function.
function _isElementVisible(el, parent) {
var eap,
rect = el.getBoundingClientRect(),
docEl = parent,
vWidth = docEl.clientWidth,
vHeight = docEl.clientHeight,
efp = function (x, y) { return document.elementFromPoint(x, y) },
contains = "contains" in el ? "contains" : "compareDocumentPosition",
has = contains == "contains" ? 1 : 0x14;
// Return false if it's not in the viewport
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight)
return false;
// Return true if any of its four corners are visible
return (
(eap = efp(rect.left, rect.top)) == el || el[contains](eap) == has
|| (eap = efp(rect.right, rect.top)) == el || el[contains](eap) == has
|| (eap = efp(rect.right, rect.bottom)) == el || el[contains](eap) == has
|| (eap = efp(rect.left, rect.bottom)) == el || el[contains](eap) == has
);
}
scope.isVisible = function (index) {
if (!scope.active)
return false;
return _isElementVisible(jqElm.find('.modal li')[index], jqElm.find('.modal')[0]);
}
<li ng-repeat="item in items" ng-class="{'visible': isVisible($index)}"></li>