Hi I have situation where I have to format certain values using ng-init as well as have ng-model for two way binding in input field so value can be changed and saved.
<div ng-repeat="ab in ablist">
<div class="col-sm-3">
<input type="text" ng-init="item.ab=fn(item.ab)" ng-model="item.ab" />
</div>
</div>
The above code doesnt work. It does not show the formatted value.
Can you let me know how I can change this so I can display the formatted value using ng-init but as well as keep ng-model for binding edited value on submit.
In this case you have to use directive. ng-init is to be used when you want to set some default or initial value to any control. Directive can keep your display value and your model value in separate formats. Here is a directive for formatting integers with commas.
app.directive('formattednumber', function () {
return {
link: function (scope, element, attrs, ctrl) {
element.bind('blur', function (blurEvent) {
if (element.data('old-value') !== element.val()) {
// console.log('value changed, new value is: ' + element.val());
scope.$apply(function () {
var v = number_format(element.val(), 0, 0, 99999999999);
element.val(v);
//scope.myDirective = element.val();
//element.data('old-value', element.val());
});
}
});
ctrl.$formatters.unshift(function (modelValue) {
if(!modelValue) modelValue="0";
if(modelValue.replace &&(isNaN(parseFloat(modelValue)) || !isFinite(modelValue)))
{
modelValue = parseFloat(modelValue.replace(/,/g, ""));
}
var v = number_format(modelValue, 0, 0, 99999999999);
v = v == undefined? 0 : v;
element.val(v);
return v;
});
ctrl.$parsers.unshift(function (viewValue) {
if(!viewValue) viewValue="0";
viewValue = parseInt(viewValue.replace(/,/g, ""));
var v = number_format(viewValue, 0, 0, 99999999999);
v = v == undefined? 0 : v;
element.val(v);
return viewValue;
});
},
restrict: 'A',
require: 'ngModel'
}
});
Similarly, you can write one for date formatting. You can use Date.js for the same.
The usage is :
<input type="text" ng-model="item.amount" formattednumber />
What about this:
<div ng-repeat="ab in ablist">
<div class="col-sm-3">
<input type="text" ng-init="item=fn(ab)" ng-model="item" />
</div>
</div>
Related
I have an AngularJS (1.4) application and I'm trying to manipulate a input type="number" through a directive.
I have to use type="number" because with type="tel" on iOs the character "." does not appear in the keyboard and this make impossibile to insert decimals number.
The goal is to make the user insert maximum 3 digits before the "." and maximum 2 digits after. The number of dots must not exceed one. That's the format I want in my input field.
The problem is that if I pass I print the variable this.value in the directive, it doesn't contain any ".".
Here's the html code:
<input required type='number' limit-to-number="3" number-of-decimals="2" ng-required="true" ng-model="invoice.price">
and here's the directive:
directive("limitToNumber", [function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
var limit = parseInt(attrs.limitToNumber);
var number_of_decimals = parseInt(attrs.numberOfDecimals);
var digit_array = [];
angular.element(elem).on("keydown", function(e) {
console.log(this);
});
}
};
}]).
I tried different approaches, the main problem is that this.value does not contain ".".
For example if the input in the form is "12":
console.log(this.value) // -> 12
But if the input in the form is "......12.....":
console.log(this.value) // -> "" empty string
But I noticed that printing just "this" return the whole html of the input:
<input required type='number' limit-to-number="3" number-of-decimals="2" ng-required="true" ng-model="invoice.price" name="price" aria-invalid="false">
#shadow-root (user-agent)
<div id="text-field-container" pseudo="-webkit-textfield-decoration-container">
<div id="editing-view-port">
<div id="inner-editor">12.</div>
...
In the div with id="inner-editor" I can see the exact value of the input field. The problem now is that this is not working:
console.log(this.findElementById("inner-editor")); // -> Uncaught TypeError: this.getElementById is not a function
How can I extract that div value from the object "this" ?
You can use
<input type="number" step="0.01" max="999.99" min="0" required />
Of course you must validate input of user after submit (angular form validation foe example).
Try this:
.directive('limitToNumber', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
// inputValue variable will contain the input box value
var limit = parseInt(attrs.limitToNumber);
var number_of_decimals = parseInt(attrs.numberOfDecimals);
var digit_array = [];
if (inputValue == undefined) return '';
// Your transformation code will be written here
// Assign the transformed value to transformedInput variable
var transformedInput = '';
if (transformedInput !== inputValue) {
// This will update the input box
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
Please let me know if it helps!
something similar may have been answered (ng-pattern + ng-change) but all responses were unable to fix this issue.
I have two imbricated directives for creating a form input, a parent directive to control name, label, validator etc. and a child directive to set pattern and input type specific stuff.
However, when setting a pattern, the value on my model is set to undefined when ng-pattern return false.
Directives:
<input-wrapper ng-model="vm.customer.phone" name="phone" label="Phone number">
<input-text type="tel"></input-text>
</input-wrapper>
Generated HTML:
<label for="phone">Phone number:</label>
<input type="text" name="phone"
ng-model="value"
ng-model-options="{ updateOn: \'blur\' }"
ng-change="onChange()"
ng-pattern="/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/">
JS:
angular.module('components', [])
.directive('inputWrapper', function() {
return {
restrict: 'E',
require: 'ngModel',
scope: true,
link: function (scope, element, attrs, ngModel) {
scope.name = attrs.name;
scope.label = attrs.label;
scope.onChange = function () {
ngModel.$setViewValue(scope.value);
};
ngModel.$render = function () {
scope.value = ngModel.$modelValue;
};
}
}
})
.directive('inputText', function() {
return {
restrict: 'E',
template: '<label for="{{name}}">{{label}}:</label><input type="text" name="{{name}}" ng-model="value" ng-model-options="{ updateOn: \'blur\' }" ng-change="onChange()" ng-pattern="pattern">',
link: function (scope, element, attrs) {
if (attrs.type === 'tel') {
scope.pattern = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
}
}
}
});
angular.module('app',['components'])
.controller('ctrl',function($scope){
var vm = this;
vm.customer = {
phone: '010203040506'
};
});
What am I doing wrong ?
Codepen for use case: https://codepen.io/Yosky/pen/yVrmvw
By default in angular if a validator fail, undefined value assigned to ng-model, You can change this setting as follow :
<div ng-model-options="{ allowInvalid: true}">
read here for detail docs
I had some requirements that meant that I really really didn't want ng-model to write out undefined to the scope when validation was invalid, and I didn't want the invalid value either, so allowInvalid didn't help. In stead I just wanted ng-model do not write anything, but I couldn't find any option for this.
So I couldn't see any way forward except for doing some monkey patching of the ng-model controller.
So first I required ngModel in the component I was building require: { model: 'ngModel' } and then I did this in the $onInit hook:
const writeModelToScope = this.model.$$writeModelToScope;
const _this = this;
this.model.$$writeModelToScope = function() {
const allowInvalid = _this.model.$options.getOption('allowInvalid');
if (!allowInvalid && _this.model.$invalid) {
return;
}
writeModelToScope.bind(this)();
};
I also didn't want to take in a new model value while the value was invalid and the component had focus, so I did:
const setModelValue = this.model.$$setModelValue;
this.model.$$setModelValue = function(modelValue) {
_this.lastModelValue = modelValue;
if (_this.model.$invalid) {
return;
}
if (_this.hasFocus) {
return;
}
setModelValue.bind(this)(modelValue);
};
element.on('focus', () => {
this.hasFocus = true;
});
element.on('blur', (event) => {
this.hasFocus = false;
const allowInvalid = this.model.$options.getOption('allowInvalid');
if (!allowInvalid && this.model.$invalid) {
this.value = this.lastModelValue;
}
event.preventDefault();
});
Feel free to judge me, just know that I already feel dirty.
I was working in angular project, There I had come across a situation in which I need to validate custom component having a textbox.
<dollar-text-validate ng-model="ctrl.value" required name="myDir"></dollar-text-validate>
My Component
angular.module("myApp", []);
angular.module("myApp").controller('MyController',function(){
var ctrl = this;
ctrl.value = 56;
});
angular.module("myApp").component('dollarTextValidate',{
bindings: {
ngModel :'='
},
template: '<div><input type="text" ng-focus="ctrl.focus()" ng-blur="ctrl.blur()" ng-model="ctrl.amount1"><input type="hidden" ng-model="ctrl.ngModel"></div>',
controller: function() {
var ctrl = this;
// ctrl.amount1 =
ctrl.amount1 =ctrl.ngModel===undefined||ctrl.ngModel==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
ctrl.focus=function(){
ctrl.amount1 = ctrl.amount1 === undefined ? '' : ctrl.amount1.slice(1);
ctrl.ngModel = ctrl.amount1;
console.log(ctrl.ngModel);
}
ctrl.blur=function(){
ctrl.ngModel = ctrl.amount1;
ctrl.amount1 = ctrl.amount1==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
}
},
controllerAs:'ctrl'
})
This component is used to set $ symbol in front of entered value. So $ appended value will be available in textbox and original value which is to be validated in hidden field.
How can I validate hidden field. I tried with required attribute in hidden tag but nothing happening. Also tried with custom tag.
Sorry to break it to you, but you might wanna go for directive, and then use $parsers, $formatter and $validators properties of ngModelController.
Component can be used for this, but it is just easier with normal directive.
angular.module('myApp', []);
angular.module("myApp").directive('dollarTextValidate', function() {
return {
require: 'ngModel',
link: function($scope, $element) {
var regexp = /^\$(\d+(\.\d+)?)$/;
var ngModel = $element.controller('ngModel');
ngModel.$formatters.push(function(value) {
return value ? '$' + value : '';
});
ngModel.$parsers.push(function(value) {
var matched = value.match(regexp);
if (matched) {
return +matched[1];
}
});
ngModel.$validators.greaterThan10 = function (modelVal, viewVal) {
var value = modelVal || viewVal;
return value > 10;
};
},
controllerAs: 'ctrl'
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="myApp" ng-form="form">
dollar-text-validate = <input dollar-text-validate ng-model="value" required name="myDir"><br>
number input = <input type="number" ng-model="value" required><br>
value = {{value}}<br>
errors = {{form.myDir.$error}}
</div>
I want to assign ng-model variable based on condition. For example:
<input type="text" ng-model="item.model[multilang]" >
$scope.multilang can be "ENG","JP"(languages) or false. For example, if multilang = "ENG" and user type in input "Hello",the result will be
item.model = {ENG:"Hello"}
The problem is when $scope.multilang = false , I want the result would be
item.model = "Hello"
I can't find a way to achieve above result. I think one solution is changing ng-model based on $scope.multilang so when it's false,it will change ng-model of input to be = ng-model="item.model" but I don't know how to do this.
EDITED
I thought of one solution:
<input ng-if="multilang" type="text" ng-model="item.model[multilang]" >
<input ng-if="!multilang" type="text" ng-model="item.model" >
but there is better way to achieve ?
-----plnkr example-----
Angular is very flexible and powerful framework. You should use custom directive and ngModel's getter/setter option.
Directive without ngModel's getter/setter may look like this:
<input type="text"
ng-model="val"
multilang="multilang"
multilang-model="item.model">
Directive code:
.directive('multilang', [function(){
return {
restrict: "A",
require: "ngModel",
scope: {
multilang: "=",
multilangModel: "="
},
link: function(scope, element, attr, ngModel){
ngModel.$viewChangeListeners.push(function()){
var value = ngModel.$modelValue;
if(scope.multilang !== false) {
if(typeof scope.multilangModel == 'undefined')
scope.multilangModel = Object.create(null)
scope.multilangModel[scope.multilang] = value
}
else {
scope.multilangModel = value
}
})
}
}
}])
--forked plunkr--
In case of using ngModel's getter/setter
<input type="text"
ng-model="val"
multilang="multilang"
multilang-model="item.model"
ng-model-options="{ getterSetter: true }">
Directive code:
.directive('multilang', [function(){
return {
restrict: "A",
scope: {
multilang: "=",
multilangModel: "=",
val: "=ngModel"
},
link: function(scope, element, attr){
scope.val = function(newValue) {
if(scope.multilang !== false) {
if(typeof scope.multilangModel == 'undefined')
scope.multilangModel = Object.create(null)
return arguments.length ? (scope.multilangModel[scope.multilang] = newValue) : scope.multilangModel[scope.multilang];
}
else {
return arguments.length ? (scope.multilangModel = newValue) : scope.multilangModel;
}
}
}
}
}])
--forked plunkr--
In my opinion, second one is better. It has two way binding with item.model and changes input value when item.model was changed in other place of code.
Try this:
<input ng-show="multilang" type="text" ng-model="item.model[multilang]" >
<input ng-hide="multilang" type="text" ng-model="item.model" >
I have a page where users can rate their performance in different categories. I am using the ui-jq slider for angular and i want to bind the selected value to a model in $scope.
<div ng-repeat="competence in screening_has_skillset.skillset.competences">
<div class="col-sm-12 m-b-md">
<p class="h3">{{competence.competence.name}}</p>
{{competence.competence.level}}
<input id="slider"
ui-jq="slider"
ui-options="{
min: 0,
max: 10,
step: 1,
value: {{competence.competence.level || val }}
}"
ng-model="competence.competence.level"
class="slider slider-horizontal form-control" type="text">
</div>
</div>
I had same problem and write an attribute directive which is named as ng-slider-model for value binding.
.directive('ngSliderModel', ['$parse', function($parse) {
return {
scope: {
ngSliderModel: '=',
uiOptions: '='
},
restrict: 'A',
required: ['ngSliderModel'],
link: function(scope, element, attrs) {
// check there is uiOption or not
var options = ('uiOptions' in attrs) ? scope.uiOptions : {};
// get the value of ngSlider Model
var val = scope.ngSliderModel;
// if value is range [15,25] then return values for uiOptions propertyName else value for singles
var propName = (angular.isArray(val)) ? 'values' : 'value';
/* if you want to slide when the scope value changed not from slider...
watch the ngSliderModel attribute
*/
scope.$watch('ngSliderModel', function(newValue) {
element.slider(propName, newValue);
});
// set value for options
options[propName] = val;
// binding slide event
element.bind('slide', function() {
// Read the current value
var value = element.slider('option', propName);
scope.$apply(function() {
// Apply the value to scope
scope.ngSliderModel = value;
});
});
}
};
}]);;
<input id="slider" ui-jq="slider" value="val" ui-options="{
min: 1,
max: 100,
step: 1,
value: val
}" ui-event="{slideStop: 'updateModel($event)'}" class="slider slider-horizontal form-control" type="text">
value = "val"
do this. in your html element input set the value. where val is scope variable