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.
Related
Above all, I have the plnkr at here.
I am trying to create a series of directive that support in-place toggle of text display and edit within a form. As I understand, there is a similar module like xeditable available, but we need to do something different down the road. So I started with an experiment to start with something similar.
First, I create a directive that allows toggling edit/display by setting an attribute editEnabled on the directive called editableForm. The following code does not do anything special other than a line of log message.
function editableForm ($log) {
var directive = {
link: link,
require: ['form'],
restrict: 'A',
scope: {
editEnabled: "&editEnabled"
}
};
return directive;
function link(scope, element, attrs, controller) {
//$log.info('editEnabled: ' + scope.editEnabled());
$log.info('editEnabled: ' + attrs.editEnabled); //this also works
}
} //editableForm
Then I wrote the following directive to override the input tag in html:
//input directive
function input($log) {
var directive = {
link: link,
priority: -1000,
require: ['^?editableForm', '?ngModel'],
restrict: 'E'
};
return directive;
function link(scope, element, attrs, ngModel) {
ngModel.$render = function() {
if (!ngModel.$viewValue || !ngModel.$viewValue) {
return;
}
element.text(ngModel.$viewValue);
};
$log.info('hello from input');
$log.info('input ngModel: ' + attrs.ngModel);
// element.val('Hello');
scope.$apply(function() {
ngModel.$setViewValue('hello');
ngModel.$render();
});
}
} //input
I was trying to show the ngModel value of the input as text in the input directive, however, it doesn't seem to do anything in my testing. Could someone spot where I am doing wrong? I wish to replace each input fields with text/html (e.g. <span>JohnDoe</span> for Username).
My first attempt on input is a proof of concept. If it works, I will keep working on other tags like button, select, etc.
Long shot here... Your requiring both editableForm and ngModel in your input directive. So the fourth parameter of your link function should be an array of controllers in the respective order of the require array, not the ngModel controller as you are expecting.
I didnt go any further in examining your code, but check it out.
I have written a custom directive for select component. The problem I face is simpleComboSelectionChanged() prints the previous selected value and not the current value. Please let me know what is the problem.
directive & Controller:
.directive('simpleSelect', [function($compile) {
return {
restrict: 'E',
transclude: true,
require: '^ngModel',
scope:{
id: '#',
ngModel: '=',
items: '=',
ngChange: '&'
},
// linking method
link: function(scope, element, attrs) {
scope.updateModel = function()
{
scope.ngChange();
};
},
template:'<select class="form-control" id="id" ng-model="ngModel" ng-selected="ngModel" ng-options="Type.type as Type.name | translate for Type in items"'+
'ng-change="updateModel()"></select>'
};
}])
.controller('ComboTemplateCtrl', ['$scope', function($scope) {
$scope.ComboItems = [{type:1, name:"Combo.Item1", isSet:false},
{type:2, name:"Combo.Item2", isSet:false},
{type:3, name:"Combo.Item3", isSet:false},
{type:4, name:"Combo.Item4", isSet:false},
{type:5, name:"Combo.Item5", isSet:false}
];
$scope.simpleSelectValue = $scope.ComboItems[0].type;
$scope.simpleComboSelectionChanged = function(){
console.log("Selected Item is :", $scope.simpleSelectValue);
};
}])
<simple-select id="simpleSelectTest"
ng-model="simpleSelectValue" items="ComboItems"
ng-change="simpleComboSelectionChanged()"></simple-select>
The reason this happens is because you are binding to ngModel as opposed to require: "ngModel" and using the ngModelController API to modify it. (You are, in fact, use require, but you aren't actually using. Neither are you using transclude which is not needed here).
What happens is the inner ngModel-bound variable - scope.ngModel - is changed to the currently selected item, then the inner ng-change is fired, which invokes the outer ng-change, which tries to read the outer variable bound to ng-model attribute - $scope.simpleSelectValue. But $scope.simpleSelectValue hasn't yet changed - this will happen on a $watch that will occur later.
The main point is - this is not how ngModel is meant to be used.
ngModel is a directive that custom input control authors (like yourself) can use to integrate with other ngModel-compatible directives (such as form, ng-required, ng-change and other custom directives) that can validate, transform, or just listen to changes of input values.
Since you are building a custom input control essentially (even if you are using a built-in control under the covers), you need to support your own ngModel.
Here's a conceptual overview of how this can be done:
.directive('simpleSelect',
function($compile) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
id: '#',
items: '=',
},
link: function(scope, element, attrs, ngModel) {
ngModel.$render = function(){
scope.selectedValue = ngModel.$viewValue;
};
scope.onChange = function(){
ngModel.$setViewValue(scope.selectedValue);
};
},
template: '<select class="form-control" id="id" ng-model="selectedValue"' +
'ng-options="Type.type as Type.name for Type in items"' +
'ng-change="onChange()">' +
'</select>'
};
});
ngModel.$render is fired when rendering is required (for example, when model value changes) - all that you need to do here is to set the value bound to inner ng-model - no need even to do direct DOM manipulation.
ngModel.$setViewValue is called when the input directive receives input from the user. Again, since you are using an existing input directive <select>, then you could just rely on its ng-change.
You will also notice that there is no longer a need to have your own ng-change. That is the value of ngModel support - a consumer of your directive can just treat your input control like any other, include support for ng-change.
Read more about custom input controls.
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.
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
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