Angular phone number directive using intl-tel-input lib - angularjs

I am currently trying to create a custom directive that would initialize an input with the following lib : intl-tel-input .
So I downloaded the required .js file with bower:
<script src="bower_components/intl-tel-input/build/js/intlTelInput.min.js"></script>
Then I create my input :
input type="tel" class="form-control" id="tel" name="tel" ng-model="informations.tel" ng-keyup="checkPhoneFormat()" ng-click="checkPhoneFormat()">
And I'm initializing it at the beginning of my controller like this :
angular.element('#tel).intlTelInput({
validationScript: "../../bower_components/intl-tel-input/lib/libphonenumber/build/isValidNumber.js",
preferredCountries: ['en', 'fr']
});
My problem is that when I'm trying to access the informations.telmodel, it is always undefined. It seems that the input do not update the model value on the fly.
So I have to write something like this to bind the actual value of my input field with my non updated model value :
$scope.checkPhoneFormat = function(){
$scope.informations.telephone = angular.element('#telephone').val();
...}
It could be ok but I would like to create a custom directive to initialize such inputs, something like :
app.directive('phoneInput', function (PhoneFactory) {
return {
require: 'ngModel',
restrict: 'A',
scope: {
phoneNumber: '='
},
link: function (scope, element, attrs, ctrl) {
element.intlTelInput({
validationScript: "../../bower_components/intl-tel-input/lib/libphonenumber/build/isValidNumber.js",
preferredCountries: ['en', 'fr']
});
ctrl.$parsers.unshift(function(viewValue) {
console.log(viewValue);
});
}
};
});
But as ngModel is undefined the initialization function is never reached... Do you have any idea how I could solve my problem?

You are right that the model does not get updated automatically.
You could make a directive like this
app.directive('intlTel', function(){
return{
replace:true,
restrict: 'E',
require: 'ngModel',
template: '<input type="text" placeholder="e.g. +1 702 123 4567">',
link: function(scope,element,attrs,ngModel){
var read = function() {
var inputValue = element.val();
ngModel.$setViewValue(inputValue);
}
element.intlTelInput({
defaultCountry:'fr',
});
element.on('focus blur keyup change', function() {
scope.$apply(read);
});
read();
}
}
});
that could be called like this
<intl-tel ng-model="model.telnr"></intl-tel>
Here is a Plunker

There is a new directive called international-phone-number # https://github.com/mareczek/international-phone-number
Please checkout, any contributions are welcome

A few of us used Marks's directive, but the lack of testing and jquery's required position in the head was causing issues so ng-intl-tel-input was created:
https://github.com/hodgepodgers/ng-intl-tel-input
Check it out, it's unit and functionally tested with protractor
Play with it here:
http://hodgepodgers.github.io/ng-intl-tel-input/

Mark's directive worked for me:
https://github.com/mareczek/international-phone-number
One issue with intl-tel-input (at least with v3.6) is that it doesn't format the phone number you initialize it with properly* unless you have a plus sign ('+') before it. This leads to funky behavior for my users. I store my phone numbers normalized (without the plus sign) in my database so I need a hack to get around this. Instead of formatting on the server, I opted to format the phone number on the front-end. I added the following to Mark's directive to get the behavior I needed:
var makeSureInitialValueStartsWithPlusSign = function() {
var clear_watcher = scope.$watch(attrs.ngModel, function(changes) {
elem_val = element.val();
if (elem_val && elem_val[0] != "+") {
element.val("+" + changes);
clear_watcher();
}
});
};
makeSureInitialValueStartsWithPlusSign();
Thanks Mark
*By properly I mean converting '19734566789' to '+1 973-456-6789'. int-tel-input converts '19734566789' to '1 973-456-6789' (without the pus). When a user goes to edit it, they experience whacky behavior because the plus is not there.

Related

Dynamically added directives call formatter instead of parser

I have an usecase where I need to dynamically add directives to an input field, depending on the configuration set in a DB.
It all seemed to work fine, but there were some strange quirks with these input fields.
I discovered that the strange behaviour is caused by the directives calling the formatters when I expect them to call the parsers.
I made a plunker to demonstrate this behaviour.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.test1 = 'World1';
$scope.test2 = 'World2';
});
app.directive('test', ['$log', function($log) {
return {
require : 'ngModel',
link : function(scope, elm, attrs, ctrl) {
function parse(viewValue) {
console.log('parsing', viewValue);
return viewValue;
}
function format(viewValue) {
console.log('formatting', viewValue);
return viewValue;
}
ctrl.$formatters.unshift(format);
ctrl.$parsers.unshift(parse);
}
};
}]);
app.directive('variabele', ['$compile', function($compile) {
return {
restrict : 'E',
template : '<div><input ng-model="ngModel" /></div>',
scope : {
ngModel : '='
},
require: ['ngModel'],
link: function(scope, elm, attrs, ctrl) {
console.log('testing');
var input = angular.element(elm.find("input"));
input.attr('test', '');
$compile(input)(scope);
}
};
}]);
plunker
It's a bit simplified from what I have to illustrate the problem. There are two input fields. One of which always has the test directive. The other has the variable directive which in turn adds the test directive dynamically.
In reality one or more directives are added which are defined in the database.
When you change the value of the first input field you can see in tghe console that the parser is called, but when you change the value of the second input field you see that the formatter is being called instead. I'm not sure what I'm doing wrong here.
EDIT: The original plunker was broken, so i fixed it. They now use a different model for each input field and the second input field correctly uses the variabele directive.
It is the expected behaviour,
Formatters change how model values will appear in the view.
Parsers change how view values will be saved in the model.
In your case, you bind the same value in both directive test and variabele. when you change value in test directive parsers are called ( view -> model) and in variabele it is the other way (model -> view) formatters are called.
for more info: How to do two-way filtering in angular.js?

How to display 4 digits in the TEXT field

I'm using Angular.Js which contains Ionic. I have an
input field with the type as TEXT which contains the maxlength of
16 digits.
Now I want to display the last 4 digits of the field values and other
digits should be masked.
So can anyone please suggest me any approach on it to achieve.
To get you started: First bind the value of the Textarea to a scope variable and add a function for ng-change:
<textarea ng-model="model.myText" ng-change="maskValue()"></textarea>
Then in your controller do something like:
$scope.maskValue = function(){
$scope.model.myText = "xxxxxxxxxxxxxxxx" + $scope.model.myText.substring(16, 20)
}
maskValue() Will be called every time the content of the Textarea changes. This is surely not working "as is" but it should show the right direction.
Use a custom directive to filter the input on change event.
.directive("test", function() {
return {
require: "?ngModel",
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModel) {
elem.bind('change', function() {
console.log(ngModel);
ngModel.$setViewValue(scope.ngModel.substring(scope.ngModel.length - 4, scope.ngModel.length));
ngModel.$render();
});
},
replace: true
}
});
Using scope is not a good idea. It does not offer re-useable module pattern and also has performance issues.

using bootstrap-datepicker with angularjs. Need to find a way to update ng-model when a date is chosen

In short, I need to find a way to update ng-model when using bootstrap-datepicker. Here is a plunker I made to demonstrate what is going on http://plnkr.co/edit/nNTEM25I2xX2zRKOWbD1?p=preview. I've tried searching around and am fairly positive that I need to use a directive to pass a value to the model. Typing something in the text box will update the selected date model, but just using the datepicker does nothing. The below directive seemed like it should work but unfortunately it doesn't seem to have much of an effect.
app.directive('datepicker', function() {
return {
restrict : 'A',
require : 'ngModel',
link : function(scope, element, attrs, ngModelCtrl) {
$(function() {
element.datepicker({
dateFormat : 'dd/mm/yy',
onSelect : function(date) {
ngModelCtrl.$setViewValue(date);
element.datepicker("setDate", date);
scope.$apply();
}
});
});
}
}
});
An easy solution would be to just use another datepicker, but unfortunately due to restrictions on how many external libraries I can use this is the datepicker I have to use. Any insight would be greatly appreciated!!!
I strongly recommend using UI-Bootstrap or something similar.
But for those that need to use Bootstraps date-picker for whatever reason here is a starting place using your directive, with a few changes:
app.directive('datepicker', function() {
return {
restrict: 'A',
require: 'ngModel',
compile: function() {
return {
pre: function(scope, element, attrs, ngModelCtrl) {
// Initialize the date-picker
$(element).datepicker({
format: 'dd/mm/yyyy'
}).on('changeDate', function(ev) {
// Binds the changes back to the controller
// I also found that event.format() returns the string
// I wasn't aware of that. Sure beats using the date object and formatting yourself.
ngModelCtrl.$setViewValue(ev.format('dd/mm/yyyy'));
// I HATE using $apply, but I couldn't get it to work without
scope.$apply();
});
}
}
}
}
});
HTML:
<input type="text" datepicker="" ng-model="date" />
Very simple and straightforward and allows you to reuse, here is a working plunker

Check duplicate values for a text filed in Angular form

Folks,
I have a text-field in a form. When a user enters a value in this text field,
I would like to check it against a array of values to ensure this value doesn't already exists.
I started off writing a directive and since I am new to this I am quiet lost
<input type="text" ng-model="user.name" placeholder="Enter Name" required checkDuplicateName existing-names="allUsers" tooltip/>
I the above code allUsers looks like this
var allUsers = ['john','james','Tim'];
My directive is as follows:
angular.module('formInputs').directive('checkDuplicateName', function() {
return {
restrict : 'A',
require : 'ngModel',
link: function(scope, element, attrs, ctrl) {
scope.$watch(attrs.ngModel,function(){
for (var i =0; i<attrs.existing-names.length;i++) {
if (attrs.existing-names[i] === attrs.ngModel) {
attrs.$set('tooltip','Name already exsists');
return;
}
}
});
}
}
});
The problem I am having is that the directive is not getting fired. What am i missing here ?
I've created a fiddle here that has a working version of your idea. It was just a couple small things, it doesn't look like you are passing your data correctly. When you access attrs it just gives you the string that is in the markup so attrs.ngModel will just give you "users.name" as a string.
I created an isolate scope so that you can see the scoping.
scope: {
model: '=ngModel',
users: '=existingNames'
},
Also, angular turns this-case-in-html into camel case in the js like: thisCaseInHtml
Edit: if you don't want to isolate scope, that's fine too. You just need to be evaluating the strings on the scope instead.
Here's an updated fiddle http://jsfiddle.net/ddJ4Z/10/ with the changes. Notice how the values are evaluated on the scope.
scope.$watch(attrs.ngModel,function(newVal){
var users = scope.$eval(attrs.existingNames) || [];
for (var i =0; i<users.length;i++) {
if (users[i] === newVal) {
window.alert(newVal);
return;
}
}
});
Hope this helps!

Angular - Directive reflecting ngModel array

This is piggy-backing a little off an earlier question. I have been trying to work with directives and data from a model/array
My model looks something like this:
$scope.testModel = {
inputA:[1,2,3]
};
Then I would have inputs for each.
My directive is checking, on keyup, if the input is greater than a given number (10). If it is, then it sets it as 10.
link: function(scope, element) {
scope.$watch('ngModel',function(val){
element.val(scope.ngModel);
});
element.bind("keyup", function(event) {
if(parseInt(element.val())>10){
element.val(10);
scope.ngModel=element.val();
scope.$apply();
}
});
}
The problem is that I get an error, depending on the input:
TypeError: Cannot set property '0' of undefined
Here is the fiddle to see the error and code I have set up: https://jsfiddle.net/prXm3/3/
NOTE
I would prefer not to change the data set as I receive it directly from the server. I know I can change the model to inputA0:1,inputA1:2,inputA2:3 and get it to work, but then I would have to transform the data when my app gets it, and then re-transform it when I send to back to the server. I would prefer to leave the model as I have it set.
Since your directive is interacting with ngModel, you should work along with it in order to update both the model and the view:
angular.module('test', []).directive('myDirective', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.unshift(function(viewValue) {
if(parseInt(viewValue) > 10) {
ngModel.$setViewValue(10);
ngModel.$render();
return 10;
}
else
return viewValue;
});
}
}
});
Working jsFiddle.
Perhaps you'd be interested in checking out the following posts for further information:
NgModelController
Developer's Guide on Forms

Resources