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" >
Related
Here is my custom directive:
angular
.module('accountApp')
.directive('uniqueRecord', function($q, $timeout, $http) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$asyncValidators.uniqueRecord = function(modelValue, viewValue) {
var value = modelValue || viewValue;
var attributes = scope.$eval(attrs.uniqueRecord);
// Lookup effect by name
return $http.get(attributes.url + '/' + value + '/' + ((attributes.currentRecordName == '' || attributes.currentRecordName == 'nothing') ? '_' : attributes.currentRecordName))
.then(function resolved() {
//username exists, this means validation fails
return $q.reject('exists');
}, function rejected() {
//username does not exist, therefore this validation passes
return true;
});
};
}
}
});
Here is the HTML:
<input type="text" id="name" name="name" class="form-control form-input" ng-model="effect.name"
ng-disabled="isReadOnly" required
unique-record="{ url: '/api/effect', currentRecordName: {{currentEffectName == '' ? 'nothing' : currentEffectName}} }"
ng-uniqueRecord-err-type="duplicateRecord"/>
As you can see in the above HTML, I am passing the value of currentRecordName to directive. In directive the value of url is passed as is but the value of currentRecordName is always undefined. Why?
The problem comes from your utilisation of mustache in the evaled expression.
An evaled expression is already considered as an angular expression and does not need the mustache.
I suggest you to change your html to :
<input type="text" id="name" name="name" class="form-control form-input" ng-model="effect.name"
ng-disabled="isReadOnly" required
unique-record="{ url: '/api/effect', currentRecordName: currentEffectName }"
ng-uniqueRecord-err-type="duplicateRecord"/>
And handle the currentEffectName vs 'nothing' directly in the directive code.
var attributes = scope.$eval(attrs.uniqueRecord);
if (!attributes.currentRecordName) {
attributes.currentRecordName = 'nothing';
}
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>
If I use ng-model for an input field and empty it, angular sets it to '' instead of null even if it was null before.
This is a problem in my case because I need to send null to the server if the input field is empty, and not ''.
Is there a way to tell angular setting "empty" input models to null?
You may want to $watch for the empty value and set it to null:
<input type="text" ng-model="test.value" />
$scope.$watch('test.value', function (newValue, oldValue) {
if(newValue === "")
$scope.test.value = null;
});
Another way would be to use a custom directive and apply a $parsers in it:
<input type="text" ng-model="test.value" empty-to-null />
myApp.directive('emptyToNull', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
ctrl.$parsers.push(function(viewValue) {
if(viewValue === "") {
return null;
}
return viewValue;
});
}
};
});
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>