Blocking the first number in a text field - javascript

I have a text input binded to a variable that contains a 5 digits number. How can I block the first digit so only the other 4 are editable?
Currently I have this: ng-model="my_var" ng-pattern="/^\d{5}$/"
Please note that the value is two-way binded, which means I'm showing it and the user can edit /save it.

You could use a custom directive that would work in conjunction with ngModel.
It could use the parser/formatter to modify the value read/printed.
Read more about it here: http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController ($formatters and $parsers)

Here is a working solution (you will need probably tweak it a little bit for your particular needs):
app.directive("filterInput", function () {
return {
require: 'ngModel', // controller to ng-model needed
restrict: 'A',
link: function (scope, element, attrs, modelCtrl) {
var unchanged = attrs.filterInput;
var regex = /^\d{0,5}$/; // the RegExp (in more generic solution could be passed from outside)
modelCtrl.$parsers.push(function (inputValue) { // adding new $parser
if(inputValue[0] != unchanged){ // if the first digit is different from what it should be set value as the first digit
modelCtrl.$setViewValue(unchanged);
modelCtrl.$render();
return unchanged;
}
if(inputValue.length > 5){ // trim the input if it is longer than five -- it is not generic at all
var rv = inputValue.substring(0,5);
modelCtrl.$setViewValue(rv);
modelCtrl.$render();
return rv;
}
var transformedInput = regex.exec(inputValue);
if (transformedInput != null) { // check if pattern exists
transformedInput = transformedInput[0];
}
else {
transformedInput = unchanged; // if not set the value to the not changeable part
}
if (transformedInput != inputValue) { // re-render if value changed
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
}
});
And how to use it:
<p>var: {{my_var}}</p>
<input type="text" ng-model="my_var" filter-input="1"/>
PLNKR
Btw: it will allow to pass only digits :-)

Perhaps not the best solution but it works:
HTML
<input ng-model="my_var" maxlength="5">
Controller
$scope.my_var = '1';
$scope.$watch('my_var', function(){
if($scope.my_var.length != 1){
if($scope.my_var.length == 0){
$scope.my_var = '1';
}
}
});

Related

Complementing of boolean not working, with ngIf in ngx-datatable

onSelect() is called whenever a row in ngx-datatable is selected. isNodeSelected for some reason is not getting complemented when the same row is selected, even though it enters that if statement. isNodeSelected is used with ngIf to display buttons. Webstorm highlights the complement statement and says 'narrowed to true'
onSelect({ selected }) {
this.isNodeSelected = true;
if (Array.isArray(this.selected) && this.selected.length === 1) {
if (this.selected[0].hasOwnProperty('id') && this.selected[0].id === selected[0].id) {
this.isNodeSelected = !this.isNodeSelected;
} else if (this.selected[0].hasOwnProperty('id') && this.selected[0].id !== selected[0].id) {
this.selected = selected;
this.isNodeSelected = true;
}
} else {
this.selected = selected;
this.isNodeSelected = true;
}
}
If this function is in your component.ts file then at least you need to replace:
onSelect({ selected }) {
with
onSelect(selected) {
as the curly brace is used to insert variables values in the template, not in the code behind.
Other than that, step through each bit with your debugger to check the values at each point.
E.g. does it enter the second if statement, if so, what is the value of selected[0].id etc.

how to convert filter to directive in angularjs

This is my old question, where I want to filter out negatives numbers from an input filed:
<input type = "text" ng-model="number"></input>
In my old question, Paritosh given me the good answer, but I need to convert that code from filter to directive. How can I convert it into a directive?
app.filter('nonNegative', function(){
return function(val){
if(val >= 0) return val; else '';
}
})
You have to just include ngmodel in directive
app.directive('myDirective', function(){
return{
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs,ngModel){
// Your filter operation
if(scope.number < 0)
scope.number = 0;
}
}
});
This will work when the element is loaded for the first time

Angularjs create input mask

I'm trying to create a directive to create custom mask for my input. I know there are other libraries I could use, but sometimes I need a custom input based on the company needs, (e.g. "OS.012-08765"), so I'd rather create my own directive.
So far I was able to format the number on the pattern I need, but not on the input, only on the model. For this example I'll be using a money input, because it's simpler to work with (I think). This is the code I'm using:
function myMoney() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModelCtrl) {
ngModelCtrl.$formatters.push( formatInput );
ngModelCtrl.$render = renderViewValue;
ngModelCtrl.$parsers.push( parseOutput );
function formatInput( value ) {
value = formatNumber(value);
return value;
}
function parseOutput( value ) {
value = formatNumber(value);
return value;
}
function renderViewValue() {
element.html( ngModelCtrl.$viewValue );
}
function formatNumber(value) {
if(!value) {
value = '000';
}
var checkValue = value.replace(/[^0-9\.]/g, '');
if(checkValue != value) {
value = checkValue;
}
value = value.toString();
if(value.length < 3) {
value = ('000'+value).slice(-3);
}
var
firstChar = value.slice(0, value.length-2),
lastChar = value.slice(-2),
firstChar = Number(firstChar).toLocaleString(),
finalChar = '$'+firstChar+','+lastChar;
return finalChar;
}
}
}
}
And this is a plunkr with the code: http://plnkr.co/edit/UjZkPFL0V4FRrDUL9jAJ?p=preview
The main problem is where the output is, if you start typing on the input, the value doesn't have the mask, is just numbers. But the model has the proper mask.
So, based on this, I have 2 main issues:
First: I want to invert these results. I want to type in the textbox and have the mask on the textbox while the model is just plain number (without the mask).
Second: If I create a button to update the value on the model, it doesn't get formatted within the mask, it stays plain text.
How can I solve these problems?
try to use ui mask, https://htmlpreview.github.io/?https://github.com/angular-ui/ui-mask/master/demo/index.html, enter AA.999-99999 under Mask Definition field to match your pattern.
<input type="text"
ng-model="serviceOrderNumber"
ui-mask="AA.999-99999"
ui-mask-placeholder
ui-mask-placeholder-char="_"/>

directive $formatters affect ngModel when writing

I have a problem to use $formatters.
My goal is to hide phone number, just leave the last 4 chars visible.
It's ok if you don't write anything in the input.
If you write something, the model is affected by the mask and I register the hidden phone in DB ...
Here's the directive I use:
.directive('tsHideField', function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attributes, controller) {
var maskValue = function (value) {
if (!value) {
return "";
}
if (value.length <= 4) {
return value;
}
var valueHide = "";
if (value.indexOf('#') === -1) {
//leave last 4 chars
valueHide = value.toString().substring(0, value.length - 4).replace(/[\S]/g, "\u2022");
return valueHide + value.toString().substring(value.length - 4);
} else {
//Adresse email, on laisse après le # et on cache tout sauf les 4 dernières lettre avant
//'lambertjer#gmail.com'.substring(0,'lambertjer#gmail.com'.indexOf('#') - 4).replace(/[\S]/g, "\u2022") + 'lambertjer#gmail.com'.substring('lambertjer#gmail.com'.indexOf('#') - 4)
valueHide = value.toString().substring(0, value.indexOf('#') - 4).replace(/[\S]/g, "\u2022");
return valueHide + value.toString().substring(value.indexOf('#') - 4);
}
// replace all characters with the mask character
//return (value || "").replace(/[\S]/g, "\u2022");
}
/** SI ON VEUT EGALEMENT CACHER A L ECRIT:
*
* var createMaskedInputElement = function() {
if (! maskedInputElement || ! maskedInputElement.length) {
maskedInputElement = element.clone(true);
maskedInputElement.attr("type", "password"); // ensure the value is masked
maskedInputElement.removeAttr("name"); // ensure the password save prompt won't show
maskedInputElement.removeAttr("core.application.main.directive.mask"); // ensure an infinite loop of clones isn't created
maskedInputElement.bind("blur", function() {
element.removeClass("ng-hide");
maskedInputElement.remove();
maskedInputElement = null;
});
$compile(maskedInputElement)(scope);
element.after(maskedInputElement);
}
};
element.bind("focus", function() {
createMaskedInputElement();
element.addClass("ng-hide");
maskedInputElement[0].focus();
});
*/
controller.$formatters.push(function (value) {
return maskValue(value);
});
}
};
});
And for your facility, here's a fiddle with a little implementation:
http://jsfiddle.net/nqp4qtLk/2/
How to prevent model to be affected by the mask ??
EDIT: I adapt the answer of Gr3g to match to my requirements
see the updated fiddle: Updated fiddle
Please see my EDITED fiddles :
If you don't allow *'s to be deleted :
Fiddle
If you allow *'s to be deleted :
Punker
Note :
If you allow *'s to be deleted, you will see in the plunker I do not allow following :
- Deleting star(s) when number(s) are visible.
- Adding a number between 2 stars or at the first position.
Code has grown up so I can only show you partial code here.
Obviously, you needed the $parsers pipeline :
controller.$parsers.push(function(val){
//Modify your value
return modifiedValue || val;
});
Notice i added 2 functions in each pipeline so I can access a String in the function where I need to modify the value. I don't have to care (too much) about casts.
controller.$parsers.unshift(function(val){
return String(val);
});
You can probably make it faster, but be careful when refactoring to think about all possibilities to handle. Especially when *'s can be deleted.
I don't think you can, imagine i go between 2 points and delete one, how will you do ?
You should use 2 differents components : one to type each character, the other showing the phone number with only 4 last displayed.
The hardest possible way : handle all key event on the input yourself so you could even resolve what i said in the beginning of my post.
You can use $parsers.push to control value to be saved in the model.
var unmask = function(value) {
var original = scope.vm.phone.toString();
var last4 = value.substring(value.length-4);
var newstr = original.substring(0, original.length-4);
return (newstr+last4);
// you can have whatever logic you want, to manipulate the original value
}
controller.$parsers.push(function (value) {
return unmask(value);
// or do what ever you want.
});
Updated fiddle- http://jsfiddle.net/anh9y8d9/3/

Replacing angular element html within a directive's link

This is my first Angular Directive.
I am trying to do a simple highlight on a html content based on the search terms used to find that content.
The problem is, that is working for the first term, but not for more. I want to all words get highlighted but I am doing something wrong when I replace the HTML content.
This is what the directive tries to do:
1.
The directive should highlight one or more words. For example.
If the search terms are "document legal" it should highlight both of them, even if they are not on this order.
So, a text like "legal something document" should get both highlighted, "legal" and "document".
2.
If the word is less than 3 characters is not going to get highlighted.
3.
If the word is not found, try removing the last character from it until its length is less than 3. You may search for "dimensions" and the search engine may return a text containing "dimension" or even "dime".
Just in case, the app is an Ionic App.
This is my code.
The angular directive:
angular.module('starter.directives', [])
.directive('highlightSearchTerms', function($compile) {
return {
restrict: 'A',
scope: true,
link: function($scope, element, attrs) {
$scope.highlightTerm = function(term) {
var html = element.html();
var highlighted = html.replace(new RegExp(term, 'gi'),
'<span class="highlightedText">$&</span>');
if (highlighted == null) {
return false;
}
// #see
// I think that the problem is here, it works the
// first time, but the second time it gets here
// the following exception is throwed
// "Cannot read property 'replaceChild' of null"
element.replaceWith(highlighted);
return html != highlighted;
};
var searchTerms = $scope.searchTerms;
if (searchTerms != undefined && searchTerms.length < 3) {
return;
}
var terms = searchTerms.split(' ');
// Try to highlight each term unless the word
// is less than 3 characters
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
// // Try to highlight unless the word is less than 3 chars
while (term.length > 2) {
// If it got highlighted skip to next term
// else remove a character from the term and try again
if ($scope.highlightTerm(term)) {
break;
}
term = term.substring(0, term.length - 1);
}
}
}
};
});
You can see some weird things. Like using $scope.highlightTerm instead of passing the highlightTerm var to the directive. I couldn't get it work.
How can I change the HTML of the element correctly?
This is the template that is using the directive:
<div ng-include src="tplName" highlight-search-terms></div>
I wish to do something like that but I couldn't get it working:
<div ng-include src="tplName" highlight-search-terms="something to highlight"></div>
Here is a Plunker:
http://plnkr.co/edit/BUDzFaTnxTdKqK5JfH0U?p=preview
I think your code is working, but the issue was that you are trying to replace the whole div that is using the directive. So what you can do is just replace element.replaceWith(highlighted); with element.html(highlighted); and it will work.
I wish to do something like that but I couldn't get it working: <div
ng-include src="tplName" highlight-search-terms="something to
highlight"></div>
You already there, just use attrs in the link function like so:
var terms = attrs.highlightSearchTerms;, and you will get what you passed in highlight-search-terms="something to highlight"
This should work for you, with using of 'compile' function:
angular.module('starter.directives', [])
.directive('highlightSearchTerms', function($compile) {
return {
restrict: 'A',
scope: true,
compile: function(elem, attrs) {
// your code
elem[0].innerHTML = '<span class="highlightedText">$&</span>';
// your code
}
};
});
Documentation also could help.
Even tough punov's solution works, I think you shouldn't trigger multiple re-compiles for a single "line". I would suggest storing the html in a variable and recompile after every term was replaced.
Here is a working example - but it needs some polishing.
http://plnkr.co/edit/3zA54A0F2gmVhCComXAb?p=preview
link: function($scope, element, attrs) {
var searchTerms = $scope.searchTerms;
var terms = searchTerms.split(' ');
$scope.highlightedHTML = element.html();
if (searchTerms !== undefined && searchTerms.length < 3) {
return;
}
$scope.highlightTerm = function(term) {
console.log("html - ", term, html);
var highlighted = $scope.highlightedHTML.replace(new RegExp(term, 'gi'),
'<span class="highlightedText">$&</span>');
//element.replaceWith(highlighted);
return highlighted;
};
function highlight(terms, compile) {
// Try to highlight each term unless the word
// is less than 3 characters
// if the term is not highlighted remove one character
// from it and try again
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
while (term.length > 2) {
var current = $scope.highlightedHTML;
$scope.highlightedHTML = $scope.highlightTerm(term);
if (current !== $scope.highlightedHTML) {
break;
}
term = term.substring(0, term.length - 1);
}
}
compile();
}
highlight(terms, function() {
element.replaceWith( $scope.highlightedHTML);
});
}

Categories