I have a very simple directive, whose purpose is to do two things:
Make a "masked" input field, from a non-angular 3rd party library work with NgModel.
Check the input value, for a valid unix time stamp.
I'm using Jasny bootstrap plugin, to create the "data masking" for a date input.
My input field:
<input ng-class="{ 'input_error' : bdayForm.bday.$invalid && !bdayForm.bday.$pristine
|| bdayForm.bday.$invalid && submitted }"
apply-scope ng-model="bday" name="bday" id="bday" type="text" placeholder=""
data-mask="99/99/9999" class="required">
Notice the data-mask attribute. This will apply the data masking functionality provided by the plugin. The apply-scope attribute is the name of directive I created. Here is the code I have so far:
app.directive("applyScope", function() {
return {
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
element.bind('keydown', (e) => {
const arr = element[0].value.split("/")
let newBday = `${arr[2]}/${arr[1]}/${arr[0]} 00:00:00`
newBday = (new Date(newBday).getTime() / 1000 + 7200) //This will convert the bday to unix time stamp.
if (!newBday) {
return false;
}
ngModel.$setViewValue(newBday);
})
}
};
});
As you can see, I'm splitting the "99/99/9999" string into an array, and then I try to construct a unix time stamp.
If it's NaN, I return false. What I would like to achieve though, is setting the input field to $invalid, when the function returns false. I need to use this $invalid property, to make my ng-class conditioning work (basically just apply red color to the input)
Can someone explain me how it should be done?
Related
I have an input field that either allows negative and positive numbers, or only positive numbers based on a value of a select.
When changing the value of the select option, I'm trying to modify the rule of the input field like this:
const id = '#myId';
$(id).attr("data-val-range-min", -10000);
removeRules(id);
addRules(id);
$(id).change(); // trying to trigger the validation of the rule
The removeRules is a function:
let removeRules = function removeRules(field) {
$(field).rules('remove');
}
And so is the addRules:
let addRules = function addRules(field) {
let $field = $(field);
if ($field.attr("data-val-required")) {
$field.rules("add", {
required: true,
messages: {
required: $field.attr("data-val-required")
}
});
}
if ($field.attr("data-val-number")) {
$field.rules("add", {
number: true,
messages: {
number: $field.attr("data-val-number")
}
});
}
if ($field.attr("data-val-range")) {
$field.rules("add", {
range: [$field.attr("data-val-range-min"), $field.attr("data-val-range-max")],
messages: {
range: $field.attr("data-val-range")
}
});
}
}
When I change the select in the UI, the data-val-range-min attribute is set correctly, but the rule is not reapplied.
Only when I manually click into the input-field and deselect it again, the rule is applied...
What am I doing wrong here?
Thanks in advance
Only when I manually click into the input-field and deselect it again, the rule is applied...
There's a validation trigger you expect that isn't part of the plugin.
By default, this plugin triggers validation on:
onfocusout - when you leave an element
onkeyup - when you're typing inside a text box
onclick - interactions with radio, checkbox, and select
Adding and removing the rules though is not enough... you'll also need to force a validation test after adding or removing the rule.
Simply call the .valid() method on the element or form when you want to programmatically force validation. Since your OP contains zero HTML markup or working demo, I cannot be more specific with a solution.
I'm using cleave.js for a date input field in my Vue.js project.
The option that I passed was this:
<cleave :options="{
date: true,
datePattern: ['m', 'd','Y']
}" id="date-input" placeholder="MM/DD/YYYY" type="text"></cleave>
How do I set maximum value for Y ?
Cleave is an input formatting library and really nothing more. It's up to you to determine how you want to limit user input. Fortunately it offers an api for accessing the underlining raw input value. The API can be found here.
Youll need to write an event listener to check the users input and cap the value as you need.
I did that for card security number and working fine
const cardCCV = new Cleave("#cardCCV", {
numeral: true,
stripLeadingZeroes: false,
onValueChanged: function (e) {
const maxSize = 3;
if (e.target.rawValue.length > maxSize) {
cardCCV.setRawValue(e.target.rawValue.substring(0, maxSize));
}
},
});
I am trying to create a directive named availableTo that can switch between two different templates depending on some message. For example, if the field is an input with the ng-model directive, I would first need to change it to read-only using the <span> tag. So far, my code can switch the view to read-only, but I cannot seem to switch it back to input:
var directive = {
restrict: 'A',
require: '?ngModel',
link: linkerFn,
replace: true
};
function linkerFn(scope, element, attrs, ngModelCtrl) {
var clonedElement = angular.copy(element);
var preOuterHTML = clonedElement[0].outerHTML; //this can save the <input> field html code
scope.$on('mode_changed', function() {
var curUserRole = userservices.getUserRole();
if (attrs.availableTo == curUserRole) {
var e = $compile(preOuterHTML)(scope);
element.replaceWith(e);
} else {
var template = '<span>' + ngModelCtrl.$viewValue + '</span>';
var e = $compile(template)(scope);
element.replaceWith(e);
}
}); //scope.$on
} //linkerFn
For an input field:
<input name="test1" class="form-control" ng-model="name" placeholder="Name 1" available-to="ADMIN"/>
I also noticed that once I change the template in the else block above, the element re-renders, and the preOuterHTML does not contain the original element html any more. This seems to be mission impossible to me, but I would like to hear some expert opinions. Thanks
element.replaceWith(e); Don't do that. In Angular, if you find yourself attempting to modify the DOM directly, you are by definition doing it wrong. You gotta sit back and let Angular do the work.
If you need to replace a directive's entire template, a fairly straightforward approach is to use ng-include with a scope variable containing the desired conditional templateUrl, e.g.
var directive = {
// ...
template: '<div ng-include="myTemplateUrl"></div>',
link: function(scope, el) {
if (/* whatever */) {
scope.myTemplateUrl="templates/foo.html";
} else {
//...etc
}
},
};
(This does add an extra DOM node to the tree, but that's generally harmless.)
It sounds like in your case you may not need to go that far, though; a simple ng-if inside your template is probably enough to swap between your read-only <span> and <input>.
Hi I am new to AngularJs and trying hard to find a solution for this. I am just learning directives and have been able to get my directive to work showing a textbox for the user to type into. The logic to change to upper and lower case characters from a string is what I'm puzzled with.
I thought maybe something like:
if (inputValue % 2 == 0) {
//have the user input.ToUpperCase()
}
E.g if user types in computer the textbox would dynamically update as the user types to CoMpUtEr.
Any help is greatly appreciated.
The best you can do in this case is to write custom directive. You need to make sure that the model is properly transformed in all cases: through input in the field, as well as when the model changes in code like $scope.model = 'some' - then it's supposed to get translated to SoMe in the view.
Here is a basic directive I wrote to alternate characters case.
var app = angular.module('app', []);
app.directive('capitalizeAlternate', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
function formatter(value) {
if (value) {
for (var i = 0; i < value.length; i++) {
if (i % 2 == 0) {
value = value.substr(0, i) + value[i].toUpperCase() + value.substr(i + 1);
}
}
return value;
}
}
ngModelController.$parsers.push(function(value) {
if (value) {
ngModelController.$viewValue = formatter(value);
ngModelController.$render();
return ngModelController.$viewValue;
}
});
ngModelController.$formatters.push(formatter);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<input capitalize-alternate type="text" ng-model="input" /> {{input}}
</div>
On your input add the handler ng-change="unformatMyText". Then add this funtion to your view, like unformatMyText: function(txt){}. Within the function body add your code. This should do the trick every time.
I have the following variable:
$scope.pixelWidth = "30px";
And I have an input box like so:
<input ng-model="pixelWidth" />
I'd like for the input box to only have the numbers inside it but still insert the px into $scope.pixelWidth while typing.
Is there a way to accomplish this?
Yes, you need to create a directive and add formatters and parsers to the ngModelController. See working version on plunker
Directive:
app.directive('modelSuffix', [function() {
return {
restrict: 'AE',
require: '^ngModel',
link: function(scope, element, attributes, ngModelController) {
var suffix = attributes.modelSuffix;
// Pipeline of functions called to read value from DOM
ngModelController.$parsers.push(function(value) {
return value + suffix;
});
// Pipeline of functions called to display on DOM
ngModelController.$formatters.push(function(value) {
return value.replace(suffix, '');
});
}
}
}]);
And use it like so:
<input ng-model="pixelWidth" model-suffix="px"/>
<input type="text" name="userName" ng-model="pixel.value" ng-model-options="{ getterSetter: true }" />
var _myPixel = '0';
$scope.pixel = {
value: function(pixel) {`enter code here`
// Note that pixelcan be undefined for two reasons:
// 1. Because it is called as a getter and thus called with no arguments
// 2. Because the property should actually be set to undefined. This happens e.g. if the
// input is invalid
return arguments.length ? (_myPixel = pixel.split("px")[0]) : _myPixel + "px";
}
};
I'm removing the "px" in the setter and adding the "px" in the getter.
I hope this work for you!
You can do this by watch funciton.
$scope.$watch("pixelWidth",function(VariableValue){
// remove "px" from your variable and assign it again
$scope.pixelWidth=newValue;
});
I don't see any way that you can accomplish this without using a second variable in your controller. If you change $scope.pixelWidth to include the 'px', that's going to end up in your input box. That's the magic of two-way data binding, except that in this use case the result may be less than magical to you.
You'll probably need to do something like react to the ng-change event on the input box to change a second shadow variable.
<input ng-model='pixelWidth' ng-change='addPx(pixelWidth)'>
in controller JS
$scope.addPx = function(pw){
$scope.withPx = pw + 'px';
}