AngularJS - Show Custom Error Message using Directive - javascript

Directive to validate IP:PORT
myApp.directive("validateServerAddress", ['input', function (input) {
var linker = function (scope, element, attrs) {
alert('Tese');
var parts = input.split(":");
var ip = parts[0].split(".");
var port = parts[1];
return validateNum(port, 1, 65535) &&
ip.length == 4 &&
ip.every(function (segment) {
return validateNum(segment, 0, 255);
});
scope.validateNum = function(input, min, max) {
var num = +input;
return num >= min && num <= max && input === num.toString();
}
};
return {
restrict: "EA",
link: linker
};
}]);
HTML Input
<div class="input-group">
<input type="text" validate-serveraddress placeholder="255.255.255.255:5000" name="serveraddress" id="serveraddress" class="color-tooltip form-control" ng-model="serveraddress" required>
</div>
Until user types a valid IP:PORT address, I need to show custom error message 'Invalid Server Address'. The directive works well. Below could be used and custom class can be applied, but I need to show the message just like bootstrap shows for required field.
document.querySelector("input").oninput = function (e) {
this.className = validateIpAndPort(this.value) ? "" : "invalid";
}
.invalid {
color: red;
}
How can I show custom validation using ngMessages via directive as shown in below image ?

Quick directive to validate ip:port I created for you
app.directive("validateAddressPort", function() {
function validateIpAndPort(input) {
var parts = input.split(":");
var ip = parts[0].split(".");
var port = parts[1];
return validateNum(port, 1, 65535) &&
ip.length == 4 &&
ip.every(function (segment) {
return validateNum(segment, 0, 255);
});
}
function validateNum(input, min, max) {
var num = +input;
return num >= min && num <= max && input === num.toString();
}
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.invalidaddressport = function(input) {
if(!input) {
return false;
}
return validateIpAndPort(input);
}
}
};
});
html:
<form name="myForm">
<div class="input-group">
<input type="text" ng-model="serveraddress" validate-address-port placeholder="255.255.255.255:5000" name="serveraddress" id="serveraddress" class="color-tooltip form-control" ng-model="serveraddress" required>
</div>
</form>
and now, you can use ngMessages like:
<div ng-messages="myForm.serveraddress.$error" style="color:red" role="alert">
<div ng-message="required">You did not enter a field</div>
<div ng-message="invalidaddressport">Invalid Address:port</div>
</div>
my plunkr: https://plnkr.co/edit/KI9jAqPRkLTYm5EvBKza?p=preview

Related

angular directive is not working

I have a text box and a directive to restrict showing negative values in on the screen, and the ng-model should contain that negative value.
In the below directive I am always getting "" as the value for inputValue, How to get the ng-model value for the inputValue
app.directive('numberOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if (inputValue == undefined) return '';
var transformedInput = inputValue.replace(/[^0-9]/g, '');
if (transformedInput != inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
Your code works well:
HTML
<div ng-app="myModule">
<div ng-controller="myController">
<input type="text" number-only ng-model="model"/>
</div>
</div>
JS
var module = angular.module("myModule", []);
module.controller('myController', ['$scope', function($scope) {
$scope.model = '01234';
}]);
module.directive('numberOnly', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function(inputValue) {
// Value prints here
console.log(inputValue);
if (inputValue == undefined) return '';
var transformedInput = inputValue.replace(/[^0-9]/g, '');
if (transformedInput != inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
Demo
https://jsfiddle.net/bdmqxr5y/720/

Html input max length makes the text area to be 0

I am using a directive to handle decimal functionality in my html input box as well as i have given the max length for the input type of number to be 5 when i type a value more than 5 digits the existing value in the text box becomes zero can anyone tell me why it occurs or how could i avoid this.
Decimal Directive:
'use strict';
angular.module('main')
.directive('decimalpoint', function (CommonService) {
'use strict';
return {
restrict: 'A',
// require: 'ngModel',
scope: {
ngModel: '=ngModel'
},
link: function (scope, ele, attrs) {
ele.bind('keypress keyup', function (e) {
console.log('keycode: ', e.keyCode);
var newVal = ele.val() + (e.charCode !== 0 ? String.fromCharCode(e.charCode) : '');
if (ele.val().search(/(.*)\.[0-9][0-9]/) === 0 && newVal.length > ele.val().length) {
console.log('going to preventDefault');
e.preventDefault();
}
});
scope.$watchCollection('ngModel', function (newValue, oldValue) {
console.log('newValue: ', newValue);
console.log('oldValue: ', oldValue);
if (isNaN(newValue)) {
scope.ngModel = oldValue;
} else {
console.log('new value is', newValue);
console.log('type of new value', typeof (newValue));
if (newValue) {
console.log('new value string', newValue.toString());
console.log('split', newValue.toString().split('.'));
var v = newValue.toString().split('.');
console.log('v', v);
console.log('First value', v[0]);
console.log('Second value', v[1]);
var output = v[0];
if (v[1]) {
output = output + '.' + v[1].substring(0, 2);
scope.ngModel = parseFloat(output);
} else {
scope.ngModel = parseFloat(output);
}
}
}
});
}
};
})
Html page :
<label class="item item-input InputFormFull" ng-if="vm.product.selectedtype !== 'piece'">
<span class="input-label"> {{'count_message' | translate}} </span>
<input stopccp decimalpoint ng-model="vm.product.count" maxlength="8" placeholder="0" type="number" ng-change="vm.onTotalCost()" oninput="this.value = this.value.replace(/(\..*)\./g, 0);" ng-click= "vm.hideScrollContent()" />
</label>
<label class="item item-input InputFormFull">
<span class="input-label"> {{'rate_message' | translate}} </span>
<input stopccp decimalpoint ng-model="vm.product.rate" placeholder="0" type="number" ng-change="vm.onTotalCost()" maxlength="5" ng-click= "vm.hideScrollContent()"/>
</label>
Check ng-model-options allow-invalid.
If the model is invalid it gets nulled unless you specify allow-invalid.
https://docs.angularjs.org/api/ng/directive/ngModelOptions#model-updates-and-validation

How init a controller variable from the view? (Angular JS)

I'm working with a custom label witch controller contains a max value, this is for a horizontal bar that reperesents a percentage.
Since the max value for the horizontal bar it's defined dynamically I need to set it directly on the view, so when the ng-repeat starts, it set the caorrect max value.
Currently i have it like this (the view):
<div class="scroll">
<table class="tbody">
<tr ng-repeat="specialItem in specialItems">
<td class="tcellsmall alg_justify" title="{{specialItem.label | translate}}">
<label class='{{specialItem.class}}' ng-click="openLightbox(specialItem)">{{specialItem.label | translate}}</label>
</td>
<td class="tcell" ng-repeat="area in areas">
<span ng-if="specialItem[area] > 0">{{specialItem[area]}}</span>
<horizontal-bar type="{{item}}" value="{{specialItem[area]}}" ng-init="hValues.maxValue = specialItem.itemTotal " ng-if="specialItem[area] > 0" ng-cloak ng-click="clickMeter(area, specialItem.type, specialItem.custom)" area="{{area}}"/>
</td>
<td class="tcellsmall alg_right" ng-click="clickMeter('Plant', specialItem.type, specialItem.custom)">
<label class="cur_pointer">{{specialItem.itemTotal}}</label>
</td>
</tr>
</table>
</div>
And my controller is:
app.directive('horizontalBar', function () {
return {
restrict: 'E',
scope: {
type: '#',
value: '#',
barColor: '#',
threshold: '#', // only used by the Restriktionsverfolgung page
area: '#'
},
templateUrl: 'views/tools/horizontalBar/horizontalBar.html',
replace: true,
controller: ['$scope', '$rootScope', function ($scope, $rootScope) {
$rootScope.hValues = {};
var min = 0;
$rootScope.hValues.maxValue = 0;
var max = $rootScope.hValues.maxValue;
// var max = 300;
var ranges = new AssemblyLive.Models.RangeCollection();
// Init component
$scope.valuePercent = 0;
if ($scope.value !== undefined) {
$scope.$watchCollection('value', function (newValue) {
$scope.SetValue($scope.value);
});
}
// If data is found in the Config file, load it
if (Config.Areas[$scope.type] !== undefined) {
min = Config.Areas[$scope.type].minimum;
max = Config.Areas[$scope.type].maximum;
if (Config.Areas[$scope.type].ranges !== undefined) {
for (var u in Config.Areas[$scope.type].ranges)
ranges.Add(new AssemblyLive.Models.Range(parseFloat(Config.Areas[$scope.type].ranges[u].from), parseFloat(Config.Areas[$scope.type].ranges[u].to), Config.Areas[$scope.type].ranges[u].style));
}
}
//Functions
$scope.SetColor = function (color) {
$scope.backgroundColor = color;
};
$scope.SetValue = function (value) {
value = Math.round(value);
if (value <= min) value = min;
if (value >= max) value = max;
$scope.valuePercent = value / max * 100;
$scope.color = ranges.getValue($scope.valuePercent);
// Only for the Restriktionsverfolgung page
if ($scope.threshold !== undefined) {
if ($scope.valuePercent >= Number($scope.threshold))
$scope.color = "div_error";
else if ($scope.valuePercent >= Number($scope.threshold - 2))
$scope.color = "div_warning";
else
$scope.color = "div_success";
} else if (Utils.isEmpty($scope.color)) {
if (!Utils.isEmpty($scope.barColor)) {
$scope.color = $scope.barColor;
} else {
$scope.color = "grayfill";
}
} else {
$scope.color = $scope.barColor;
}
};
// Meter was clicked
$scope.clickMeter = function () {
if ($scope.type !== '') {
if (Config.Areas[$scope.type] !== undefined && Config.Areas[$scope.type].onClickURL !== undefined) {
window.location.href = Config.Areas[$scope.type].onClickURL;
}
}
};
}],
controllerAs: 'hmCtrl',
link: function ($scope, element, attrs) {
}
};
});
As you can see in the code, I have my $rootScope variables ready for set the value on the view, but it's not working.
Do you have any idea why?
----update----
Using a plugin for check the scopes in firefox, i can see that the max value is been set correctly.
Thanks
Using a plugin for check the scopes in firefox, i can see that the max value is been set correctly.

AngularJS - Blur + Changed?

What is the easiest way to combine ng-changed and ng-blur?
I've found this post: How to let ng-model not update immediately?
However, this does no longer work in angluar 1.2+
Is there any way to achieve the same behavior?
I guess I have to store a copy of the old value myself and compare the new value to that on blur if I try to do the same, or is there any easier way ?
Use ng-model options.
Like this, ng-change will only trigger when the input is blurred.
<input type="text"
ng-model="a.b"
ng-model-options="{updateOn: 'blur'}"
ng-change="onchange()"/>
This does what I want.
It stores the value on focus, and compares it to the new value on blur, if changed, it triggers the expression in the attribute.
app.directive('changeOnBlur', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ngModelCtrl) {
if (attrs.type === 'radio' || attrs.type === 'checkbox')
return;
var expressionToCall = attrs.changeOnBlur;
var oldValue = null;
elm.bind('focus',function() {
scope.$apply(function() {
oldValue = elm.val();
console.log(oldValue);
});
})
elm.bind('blur', function() {
scope.$apply(function() {
var newValue = elm.val();
console.log(newValue);
if (newValue !== oldValue){
scope.$eval(expressionToCall);
}
//alert('changed ' + oldValue);
});
});
}
};
});
usage:
<input ng-model="foo" change-on-blur="someFunc()" />
How about this solution. Works for me:
<input ng-init="oldValue = value" ng-model="value"
ng-blur="oldValue != value && callYourFunc(foo)">
I am using AngularJs 1.2.x and stumbled upon the ng-change issue of firing on each change. ng-blur can be used but it fires even though there is no change in the value. So both cannot be used efficiently.
With Angularjs 1.3.x, things are easier using ng-model-options like below
to invoke change function "onBlur"
ng-change="ctrl.onchange()" ng-model-options="{updateOn: 'blur'}"
And
to delay invocation of change function by 500ms
ng-change="ctrl.onchange()" ng-model-options='{ debounce: 500 }'"
Now coming to back to the question of getting such things with AngularJs 1.2.x
to invoke change function "onBlur"
html
<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange()" />
or
<input type="text" ng-model="ctrl.a.c" sd-change-on-blur="ctrl.onchange(ctrl.a.c)" />
JS
app.directive('sdChangeOnBlur', function() {
return {
restrict: 'A',
scope: {
sdChangeOnBlur: '&'
},
link: function(scope, elm, attrs) {
if (attrs.type === 'radio' || attrs.type === 'checkbox')
return;
var parameters = getParameters(attrs.sdChangeOnBlur);
var oldValue = null;
elm.bind('focus', function() {
scope.$apply(function() {
oldValue = elm.val();
});
})
elm.bind('blur', function() {
scope.$apply(function() {
if (elm.val() != oldValue) {
var params = {};
if (parameters && parameters.length > 0) {
for (var n = 0; n < parameters.length; n++) {
params[parameters[n]] = scope.$parent.$eval(parameters[n]);
}
} else {
params = null;
}
if (params == null) {
scope.sdChangeOnBlur();
} else {
scope.sdChangeOnBlur(params)
}
}
});
});
}
};
});
function getParameters(functionStr) {
var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
var params;
if (paramStr) {
params = paramStr.split(",");
}
var paramsT = [];
for (var n = 0; params && n < params.length; n++) {
paramsT.push(params[n].trim());
}
return paramsT;
}
to delay invocation of change function by 500ms
html
<input type="text" ng-model="name" sd-change="onChange(name)" sd-change-delay="300"/>
OR
<input type="text" ng-model="name" sd-change="onChange()" sd-change-delay="300"/>
JS
app.directive('sdChange', ['$timeout',
function($timeout) {
return {
restrict: 'A',
scope: {
sdChange: '&',
sdChangeDelay: '#' //optional
},
link: function(scope, elm, attr) {
if (attr.type === 'radio' || attr.type === 'checkbox') {
return;
}
if (!scope.sdChangeDelay) {
scope.sdChangeDelay = 500; //defauld delay
}
var parameters = getParameters(attr.sdChange);
var delayTimer;
elm.bind('keydown keypress', function() {
if (delayTimer !== null) {
$timeout.cancel(delayTimer);
}
delayTimer = $timeout(function() {
var params = {};
if (parameters && parameters.length > 0) {
for (var n = 0; n < parameters.length; n++) {
params[parameters[n]] = scope.$parent.$eval(parameters[n]);
}
} else {
params = null;
}
if (params == null) {
scope.sdChange();
} else {
scope.sdChange(params)
}
delayTimer = null;
}, scope.sdChangeDelay);
scope.$on(
"$destroy",
function(event) {
$timeout.cancel(delayTimer);
console.log("Destroyed");
}
);
});
}
};
}
]);
function getParameters(functionStr) {
var paramStr = functionStr.slice(functionStr.indexOf('(') + 1, functionStr.indexOf(')'));
var params;
if (paramStr) {
params = paramStr.split(",");
}
var paramsT = [];
for (var n = 0; params && n < params.length; n++) {
paramsT.push(params[n].trim());
}
return paramsT;
}
plnkrs for both approaches are
http://plnkr.co/edit/r5t0KwMtNeOhgnaidKhS?p=preview
http://plnkr.co/edit/9PGbYGCDCtB52G8bJkjx?p=info
how about this plunkr?
using angular's built in ng-blur, update your "persisted value" on blur
<input type="text" ng-model="boxValue" ng-blur="doneEditing(boxValue)">
when saving, verify the value is different
$scope.doneEditing = function(v) {
if (v !== $scope.persistedValue) // only save when value is different
$scope.persistedValue=v;
}
There is no special option on ng-blur for pre-checking equality that I'm aware of. A simple if seems to do the trick
Newer versions of AngularJS (now in 1.3 beta) supports this natively. See my answer here
The solution that worked for me was as follows:
<input id="fieldId" type="text"
ng-model="form.fields.thisField"
ng-model-options="{ updateOn: 'blur' }"
ng-change="form.save()" />
I found this solution here
That page says it requires AngularJS version 1.3.0+ . I tested it with AngularJS version 1.5.11 and it works in that version for me.
In app.html
<input type="text" name="name" ng-model="$ctrl.user.name" ng-blur="$ctrl.saveChanges()" ng-change="$ctrl.onFieldChange()"/>
in app.ts
public onFieldChange() {
this.isValuesModified = true;
}
//save changes on blur event
public saveChanges() {
//check if value is modified
if(this.isValuesModified){
//Do your stuff here
}
}
This is how I resolved this.
<input ng-focus="oldValue=value" ng-model="value" ng-blur="oldValue!=value && youFunction()" />

Why is my input type date value not set using Angular?

I have this HTML:
<input type="date" date="MM-dd-yyyy" required data-ng-model="customerFormData.customer.BirthDate" />
And this is my directive (from this fiddle of P. Kozlowski)
app.directive('date', function (dateFilter) {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
debugger;
var dateFormat = attrs['date'] || 'yyyy-MM-dd';
var minDate = Date.parse(attrs['min']) || 0;
var maxDate = Date.parse(attrs['max']) || 9007199254740992;
ctrl.$parsers.unshift(function (viewValue) {
var parsedDateMilissec = Date.parse(viewValue);
if (parsedDateMilissec > 0) {
if (parsedDateMilissec >= minDate && parsedDateMilissec <= maxDate) {
ctrl.$setValidity('date', true);
return parsedDateMilissec;
}
}
// in all other cases it is invalid, return undefined (no model update)
ctrl.$setValidity('date', false);
return undefined;
});
ctrl.$formatters.unshift(function (modelValue) {
return dateFilter(modelValue, dateFormat);
});
}
};
});
But my date field doesn't get a value, it keeps saying dd-mm-jjjj (Duth notation).
I set the value of customerFormData.customer.BirthDate in the code to new Date().getTime(); to test this, but like I said, it doesn't work.
What I want to achieve is that a input type date is bind to my date fields.

Categories

Resources