I want to assign value to ng-model if it is empty. I have assign default value to ng-model input from controller but when user makes change to remove that value and makes input empty then I want to assign default value to ng-model.
For example.
Controller:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
}]);
View:
<input ng-model="assignOne" ng-change="changeMe()"/>
Now, input value becomes 16.
If user make changes and manually removes this value 16 then I want default value 1 instead of empty string in changeMe() function.
Note:
I can check for empty ng-model and override value of ng-model in controller. But, Is there any way better way to use, something which angular provides.
I am just new to AngularJS.
Try $watch as follows
$scope.$watch(
"assignOne",
function handleChange( newValue, oldValue ) {
console.log( "assignOne:", newValue );
if(newValue == '') {
newValue = 16;
}
}
);
another solution to meet your sample:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
// function called by " ... ng-change="changeMe() "
$scope.changeMe = function() {
if ($scope.assignOne == '') {
$scope.assignOne = 1;
}
}
}]);
PS: for the previous answer, there is a little miss: it doesn't change the "assignOne" var, but only a local copy, which is lost at the end of the function call... Correct it by changing $scope.assignOne !
PS 2: the 2 solutions are good, but can resolve different requirements :
$watch is like a trigger on the model object, and is the most efficient if you want check business rules on the navigator side
ng-change is more interesting for handling some interactions, which are not stricts rules...
The 2 ways could be used...
Related
I am trying to create an input box that only accepts Hex characters [A-Fa-f0-9].
I know there are a lot of options but have found none that ticks all my checkboxes.
Need to restrict actual user input (so characters other than A-Fa-f0-9 will NOT be allowed entry/keypress/paste
I know this can be done through keyup/keydown/onchange event, but I need this to be enabled globally, more elegant solution
Another option is the input pattern, but this still allows user entry of 'invalid' characters, will just display later on that the validation has failed.
My thoughts:
Top choice: If it is possible to extend the input type:
<input type="hex">
If possible to have a vue directive for this (supposing q-input is my component):
<q-input v-hex>
So I created this vue directive that removes all non-hex characters and converts letters it to uppercase:
Vue.directive('hex', {
update: function (el, binding) {
console.log('Input ' + binding.value)
var newVal = binding.value.replace(/[^a-fA-F0-9\n\r]+/g, '')
console.log('New Hex Only Val = ' + newVal.toUpperCase())
binding.value = newVal
}
})
But it doesn't update the parameter passed in the v-model (parameter holding the binding.value).
Here is my fiddle:
https://jsfiddle.net/keechan/nade9cy0/3/
Sample input -> expected output:
123abc -> 123ABC
12hf23dn -> 12F23D
How can I update its respective v-model parameter? Please note that this directive will be used globally so no hardcoding anywhere.
Anyone help?
Thanks
Vue.directive('hexonly', {
bind(el, binding, vnode) {
const vm = vnode.componentInstance;
el.__unbindHexonly = vm.$watch('value', value => {
vm.__emitValue(value.replace(/[^a-fA-F0-9\n\r]+/g, '').toUpperCase());
}, { immediate: true });
},
unbind(el) {
el.__unbindHexonly && el.__unbindHexonly();
}
});
https://jsfiddle.net/6bw0zvdm/
Quasar uses some deferred model update magic, thus using specific private method __emitValue (source)
By the way, you don't need to specify directive expression ("txt"), v-hexonly will work.
I'm working on a data heavy Angular project in which I have a ng-repeat where many values get updated every second. If a value is empty, it now updates the DOM to show an empty value; which is correct behaviour.
Needed solution
What I want is a filter or expression which doesn't update the value in the DOM when the new value is empty or NULL. Latching data, I believe they call it.
Possible solutions
I found a couple of possible solutions with $watch, but I believe they are not suitable in ng-repeat, or at least not efficient:
angularjs $watch old value and new value are the same
Example of what I would like to achieve: (this does not work)
app.filter('latchIt', function () {
return function (valueOld, valueNew) {
if (valueNew == '') {
// Do not update the DOM for this value, perhaps a return of the old value
return valueOld;
} else {
return valueNew;
}
};
});
HTML
<div class="item" ng-repeat="item in items track by item.id">
<div class="value" ng-repeat="value in item.data">{{ value | latchIt }}</div>
</div>
Thanks in advance for any help & advice you can give me.
I'd create a directive for that:
codepen: http://codepen.io/anon/pen/EVXgjz
angular.module('app').directive('latched', [function() {
return {
template: '<span>{{saved}}</span>',
link: function($scope) {
$scope.$watch('value', function(val) {
if (!$scope.saved) {
$scope.saved = val || 'Not defined yet';
}
if (val) {
$scope.saved = val;
}
})
},
scope: {
value: '='
}
}
}])
Well I would say that $watch option is good enough to use it, but if you are agains it you can try to combine old collection values and new collection values according to your business rules in controller/directive and after than pass it to view.
I am trying to customize this working http://jsfiddle.net/markcoleman/JNqqU/ ,in current working fiddle directly object is assigned . where i am trying to change it to $scope.obj.items . passing object to directive is not working .
do i need to write some $watch fo r the variable ??? i am getting dynamic value that's why i am trying to pass Object value with this .
Code ::
<a href="#" pop-over items="obj.items", title="Mode of transport">
Show Pop over</a>
javascript Directive part ::
scope: {
items: '=',
title: '#'
}
Any suggestion ,
I am trying Below Fiddle
http://jsfiddle.net/JNqqU/652/
You can change your controller to this:
bootstrap.controller('maincnt', function ($scope) {
$scope.obj = { // declare the scope object here with a blank items
items: []
};
$scope.updateitem = function () {
alert('scope update called');
$scope.obj.items = ['car', 'truck', 'plane', 'bike']; // now update here
}
});
Checkout fiddle.
Yes you should be making a watcher.
$scope.$watchCollection('items', function (newValue, oldValue) {
if (newValue) {
buildTemplate(newValue);
}
});
Note: I used watchCollection because it is an array. If it were an object or simple value $watch would be used instead.
You don't need to wrap it into object, but don't 'rewrite' whole array in 'update' method, but push values into it:
bootstrap.controller('maincnt',function($scope){
$scope.items = [];
$scope.updateitem=function(){
alert('scope update called');
$scope.items.push('car', 'truck', 'plane', 'bike');
}
});
http://jsfiddle.net/btfu30k2/1/
$watch isn't necessary too.
You need two changes:
Change in HTML items :: {{obj.items}}
Change in Controller default obj items should be assigned with empty array ( $scope.obj={items:[]}; ) as popOver's $compile is looking for scope.items
See this Working fiddle
Also your testing code {{items | json }} in template can be removed after your observation.
What I'm trying to do:
I am trying to dynamically update a scope with AngularJS in a directive, based on the ngModel.
A little back story:
I noticed Angular is treating my ngModel strings as a string instead of an object. So if I have this:
ng-model="formdata.reports.first_name"
If I try to pull the ngModel in a directive, and assign something to it, I end up with $scope["formdata.reports.first_name"]. It treats it as a string instead of a nested object.
What I am doing now:
I figured the only way to get this to work would be to split the ngModel string into an array, so I am now working with:
models = ["formdata", "reports", "first_name"];
This works pretty good, and I am able to use dynamic values on a static length now, like this:
$scope[models[0]][models[1]][models[2]] = "Bob";
The question:
How do I make the length of the dynamic scope dynamic? I want this to be scalable for 100 nested objects if needed, or even just 1.
UPDATE:
I was able to make this semi-dynamic using if statements, but how would I use a for loop so I didn't have a "max"?
if (models[0]) {
if (models[1]) {
if (models[2]) {
if (models[3]) {
$scope[models[0]][models[1]][models[2]][models[3]] = "Bob";
} else {
$scope[models[0]][models[1]][models[2]] = "Bob";
}
} else {
$scope[models[0]][models[1]] = "Bob";
}
} else {
$scope[models[0]] = "Bob";
}
}
This is an answer to
I noticed Angular is treating my ngModel strings as a string instead of an object
Add the require property to your directive then add a fourth ctrl argument to your link function
app.directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attributes, ctrl) {
// Now you have access to ngModelController for whatever you passed in with the ng-model="" attribute
ctrl.$setViewValue('x');
}
};
});
Demonstration: http://plnkr.co/edit/Fcl4cUXpdE5w6fHMGUgC
Dynamic pathing:
var obj = $scope;
for (var i = 0; i<models.length-1; i++) {
obj = obj[models[i]];
}
obj[models[models.length-1]] = 'Bob';
Obviously no checks are made, so if the path is wrong it will fail with an error. I find your original problem with angular suspicious, perhaps you could explore a bit in that direction before you resort to this workaround.
I'm having some trouble getting angular to properly filter my results. I'm attempting to use a custom filter that gets arguments from a minimum input and a maximum input.
/index.html
<input ng-model="minHorsepower">
<input ng-model="maxHorsepower">
...
tr(ng-repeat="plane in planes | filter:horsepowerFilter")
/controllers.js
//Horsepower filter
$scope.horsepowerFilter = function(plane) {
var ret = true;
if($scope.minHorsepower && $scope.minHorsepower > plane.horsepower) {
ret = false;
}
if($scope.maxHorsepower && $scope.maxHorsepower < plane.horsepower) {
ret = false;
}
return ret;
};
$scope.planes = [
{
'make' : 'Piper',
'model' : 'Arrow',
'modelNumber' : 'PA-28R-180',
'horsepower' : '180',
'gear' : 'retractable',
},
{
'make' : 'Piper',
'model' : 'Arrow',
'modelNumber' : 'PA-28R-200',
'horsepower' : '200',
'gear' : 'retractable',
}
];
It works INITIALLY when I set $scope.minHorsepower/$scope.maxHorsepower in controllers.js, but only initially, not when I put something else in the <input>s. Furthermore, it prefills the inputs AND filters the results. It just doesn't work properly when I change the value of the inputs.
I've referenced this Stack Overflow thread, but I can't find any material differences in our code... AngularJS multiple filter with custom filter function
Thanks for the help.
To ensure the model values are numbers and not strings, change the type for your inputs to be number.
<input type="number" ng-model="minHorsepower" />
<input type="number" ng-model="maxHorsepower" />
Also, make sure your model values are numbers and not strings.
Alternatively, you can run everything through parseFloat(...) in your filter.
Unfortunately, I can't pinpoint exactly the problem you're having with your code. However, I think I have created a plnkr that (I believe) works as intended.
Apart from parsing the inputs with parseFloat, the logic seems to be the same. Not parsing the inputs to a numeric form shouldn't "break" it, but will possibly make it behave strangely ("9" > "10" for example).
Anyway, hopefully you can pull out the useful pieces and get it working.
Since you only send to the filter a function, it doesn't know to watch for any value changes, and thus to trigger the filter function when it happens.
When you define a filter as {{ filter_expression | filter : filterValue}}, angular watches the filterValue and triggers the filter function when it changes.
To achieve what you need you can define your own filter:
angular.module('myApp')
.filter('range', function(){
return function(items, property, min, max) {
return items.filter(function(item){
return item[property] >= min && item[property] <= max;
});
};
});
and call it like this:
ng-repeat="plane in planes | range : 'horsepower' : minHorsepower : maxHorsepower"