How can I unbind(remove) angular models when not in DOM - javascript

Here is a simple demonstration of what I'm struggling to achieve.
<div ng-controller="MyCtrl">
<input type="button" ng-click="a=!a" value="toggle a"/>
<div ng-if="a">
<input type="text" ng-model="del.a1" />{{del}}
</div>
<input type="text" ng-model="del.a2" />
{{del}}
</div>
Initially the value of del is {} and ng-if is false the property a1 is under ng-if condition.
Test Case :
step 1 : toggle the ng-if to true so that a1 is visible
step 2 : enter some value into a1 (you can anytime enter value in property a2)
step 3 : now if i again toggle ng-if to false what I'm looking for is the property a1 is to be removed from model.(i.e i just want angular to bind those models which are visible on DOM) like this
Here is the FIDDLE for the above test case.
I guess the problem is with model used as object. but I need a solution in model as object only as I have done lot of coding based on this.
Hope I'm clear with the question.
pls Help

You can watch a value using $scope.$watch and delete a1 key from del object when a is set to false
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function ($scope) {
$scope.del = {};
$scope.a = false;
$scope.$watch('a', function(value) {
if (!value) {
delete $scope.del['a1'];
}
});
})
Please see working demo below
var myApp = angular.module('app', []);
myApp.controller('MyCtrl', function($scope) {
$scope.del = {};
$scope.a = false;
$scope.$watch('a', function(value) {
if (!value) {
delete $scope.del['a1'];
}
});
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MyCtrl">
<input type="button" ng-click="a=!a" value="toggle a" />
<div ng-if="a">
<input type="text" ng-model="del.a1" placeholder="a1" />{{del}}</div>
<input type="text" ng-model="del.a2 " placeholder="a2" />{{del}}
</div>

In your example only the models visible in the DOM are watched.
If you mean you want the model value removed from the object then you would need to have a watch on 'a' that knows what values to remove from the model.

Related

How can I access the responding element where ng-disabled is attached to?

Is there a clean way to access the element where the ng-disabled property is attached to inside the function that is given to it?
Html element:
<input class="needsDisabling" ng-disabled="isFieldDisabled()" type="text">
isFieldDisabled function in controller:
How can I access the element where the ng-disabled prop is attached to in this function ?
$scope.isFieldDisabled = function () {
// How can I access the element where the ng-disabled prop is attached to in this function ?
// if (element has class "needsDisabling")
// return true;
}
I am looking for another way then passing the element as a parameter
in the function.
JSFiddle
ngDisabled was designed to work with your model (#2) and does not allow to manipulate your DOM elements manually in your controllers (which is considered a bad practice in angularjs). But anyway you can still create your custom directive and manipulate disabled property manually (#1):
angular.module('myApp',[])
.directive('myDisabled', [function () {
var myDisabled = {
restrict: 'A',
scope: false,
link : function(scope, element, attrs, ngModelCtrl){
scope.$watch(function() {
return scope.$eval(attrs.myDisabled).apply(scope, element);
}, function(val) {
element.prop('disabled', val);
});
}
}
return myDisabled;
}])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
$scope.isFieldDisabled = function (element) {
return angular.element(element).hasClass('disabled');
}
$scope.inputs = [{}, { disabled: true }, {}];
$scope.isInputDisabled = function (input){
return !!input.disabled;
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.angularjs.org/1.6.4/angular.js" ></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h2>#1</h2>
<input class="needsDisabling disabled" my-disabled="isFieldDisabled" type="text"/>
<input class="needsDisabling" my-disabled="isFieldDisabled" type="text"/>
<input class="needsDisabling disabled" my-disabled="isFieldDisabled" type="text"/>
<h2>#2</h2>
<div>
<input type="text" ng-repeat="input in inputs" ng-disabled="isInputDisabled(input)" ng-model="input.value"/>
</div>
</div>
</div>
You cannot pass the element to the controller's function in angularjs, but why not take advantage of the ng-class directive as your class and disabled values depend on each other?
ng-class="{needsDisabling: isDisabled}" ng-disabled="isDisabled"
I cannot think of a reason why you should not do it like this because;
If the class is not dynamic then there is no need for ng-disabled.
You could just use disabled="disabled" but I guess that is not the reason.
If the value of class IS dynamically switched outside of angular's
knowledge, then that part of the solution is problematic and should
be changed.
Another reason to deal with this issue in angularjs itself is to create your own directive as someone here pointed out.

Bind value of an input in controller- AngularJS

I am working with JSP and Angular JS. I have a JSP page with a hidden input field. a session attribute is set to its value as follows.
String policy = (String)session.getAttribute("POLICY_CHANGE");
<input type="hidden" value="<%=policy%>" name="policy" ng-model="$scope.policyChange" />
How can i bind the value of the input field to a variable $scope.policy in my controller.
JS
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.policyChange= ; // i want to bind the input field value here.
});
You can do this setting ng-model directive for your input:
<input type="hidden" value="<%=policy%>" name="ng2_session" ng-modal="vm.policyChange" ng-model="policy" ng-init="policy='<%=policy%>'" />
In the controller you have to use watch method.
$watch helps to listen for $scope changes
JS
app.controller('myCtrl', function($scope) {
console.log($scope.policy); // i want to bind the input field value here.
});
Simple example:
function MyCtrl($scope) {
$scope.$watch('policy', function() {
console.log($scope.policy);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<h2>Todo</h2>
<div ng-controller="MyCtrl">
<input type="text" ng-model="policy" ng-init="policy='Bob'"/>
</div>
</div>
Note: Usually, watch method it is very helpful when you want to run some code when the $scope.variable it is changed.

Enable/disable input field depending on array length in Angular 1.5

Im new to Angular 1.5. I want have an array and when it is empty, I want to disable an input field. When it's not empty, I want to enable the field. But it's not working as expected. I've put together a simple jsfiddle.
angular.module('demoApp', [])
.controller('MainController', MainController);
function MainController() {
var vm = this;
this.data = ['a', 'b'];
setInterval(function() {
if (vm.data.length === 0) {
vm.data = ['a', 'b'];
} else {
vm.data = [];
}
console.log('vm.data is now ', vm.data);
}, 2000);
}
And
<div ng-app="demoApp" ng-controller="MainController as mainCtrl">
<input type="text" ng-disabled="!data" />
<br />
data is {{ data }}
</div>
I would expect each time the setInterval() method is executed, the HTML would be updated, so the input would cycle between enabled and disabled. But doesn't work and the template isn't outputting the data array.
Since you are using controller instance you need to use its alias mainCtrl, To check if data is empty or not check its length property.
<div ng-app="demoApp" ng-controller="MainController as mainCtrl">
<input type="text" ng-disabled="mainCtrl.data.length == 0" />
<br />
data is {{ mainCtrl.data }}
</div>
Also, You should use $interval instead of setInterval.
Fiddle

Angularjs ng-required call function

It is possible make the required value dependet of some funcion?
Something like this? I want to do this because I want to change the required attribute to some form inputs...
HTML:
Name: <input type="text" ng-model="user.name" ng-required="isRequired('name')" />
Age: <input type="text" ng-model="user.age" ng-required="isRequired('age')" />
JS:
$scope.isRequired(fieldName){
$scope.requiredFields = [];
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
for (i in requiredFields) {
if (requiredFields[i] == fieldName){
return true;
}
}
return false;
}
Updated Answer:
So based on your updated OP, what you want is certainly doable. The problem with what you were trying to do is that ng-required has no ability to execute a function, it only reads a boolean. But we can dynamically create variables based on data from the server to automatically set fields to required:
Updated Plunker
<form>
Name: <input type="text" ng-model="user.test" ng-required="name" /><br/>
<input type="text" ng-model="user.name" ng-required="age" />
<br/>
<button type="submit">Submit</button>
</form>
Note that I put a $scope property for each input in the ng-required attribute. Now we can dynamically create that $scope property and set it to true if our data says we need to:
$scope.isRequired = function(){
$scope.requiredFields = [];
$http.get('fields.json')
.success(function(data){
$scope.requiredFields = angular.fromJson(data);
console.log($scope.requiredFields.required)
for (i = 0; i < $scope.requiredFields.required.length; i++) {
$scope[$scope.requiredFields.required[i]] = true
}
console.log($scope[$scope.requiredFields.required[0]]);
})
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
}
$scope.isRequired()
So it is iterating over an array of required fields received from the server, and then dynamically creating a $scope property for each one that is required, and setting it to true. Any field that has that $scope property in it's ng-required will be required now. Anything not dynamically created will just return false, and ng-required doesn't trigger.
Original answer:
Plunker
As Pratik mentioned, ng-required only accepts a Boolean value, but we can toggle the value of that with a function.
HTML
<form>
Name: <input type="text" ng-model="user.name" ng-required="isRequired" />
<br/><button ng-click="toggle()">Required: {{isRequired}}</button>
<button type="submit">Submit</button>
</form>
code:
$scope.isRequired = true;
$scope.toggle = function() {
$scope.isRequired = !$scope.isRequired;
}
I know this is a couple of years old and so AngularJS may have changed, but the accepted answer as it stands today isn't correct. You can very easily execute a function within ng-required, as it takes an expression, which can be a function. For example:
index.html
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ X ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
script.js
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
In script.js, a function addExp is defined and added to the scope, and then it's called in the ng-click directive of the a tag, which also takes an expression as its argument.
This code is taken directly from the AngularJS documentation on expressions. It doesn't use ng-require directly, but any directive that takes an expression will work the same. I have used the same syntax to use a function for ng-require.

Angularjs - assigning logic between two models

I am new to Angular. It is a very simple question -
in my index.html I am defining two models on two text boxes :-
<html><head><script...></head><body ng-app="myApp"ng-controller="MainController" >
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb2" type="text"></input>
</body></html>
And in my app.js I am defining like this
var app = angular.module('myApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.tb1 = $scope.tb2;
}]);
Now, what I want is that whatever I type in first text box (tb1) automatically typed to second text box (tb2) and vise-versa, but that is not happening.
Any guess ?
Your code in controller $scope.tb1 = $scope.tb2; would only be executed once (when controller initializes), that's why it doesn't work.
You need to bind input elements to the same model then Angular will handle two-way binding for you automatically.
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb1" type="text"></input>
Or if you want to use two different models for different elements, you can add a hook to input's ng-change event listener like
<input ng-model="tb1" type="text" name="numberofusers" ng-change="tb2 = tb1"/>
<input ng-model="tb2" type="text" ng-change="tb1 = tb2"></input>
Then these two elements would sync automatically. But you know what, ng-change can only monitor user input change, that means, if you change tb1 or tb2 programmably, ng-change will not be triggered.
In this case, you should monitor model's change using $scope.$watch
$scope.$watch('tb1', function(newValue) {
$scope.tb2 = newValue;
}));
Currently it's beyond your requirement.
This is because controller will only execute once and if there is any value in $scope.tb2 will assign to $scope.tb1 but intially both of them are blank .
So you need to $watch the changes and assign value to each other like :-
$scope.$watch('tb1',function(newVal){
$scope.tb2=newVal;
})
$scope.$watch('tb2',function(newVal){
$scope.tb1=newVal;
})
And if you want to manage it on front end you can use ng-change directive like
<input ng-model="tb1" type="text" ng-change="tb2=tb1" name="numberofusers"/>
<input ng-model="tb2" type="text" ng-change="tb1=tb2"></input>
You can use two-way binding to achieve that. An example is: JSFiddle
Create your directive:
var app = angular.module('myApp', []);
app.controller("myCtrl", function($scope) {
$scope.myName = 'Carl';
}).directive("myDirective", function() {
return {
restrict: 'AE',
scope: {
twowayBindingProp: '=myName'
}
}
});
And bind it through:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<h1>From parent: </h1>
<h3>parentProp2: <input ng-model="myName"></h3>
<div my-directive my-name="myName">
<h1>From child: </h1>
<h3>twowayBindingProp: {{ twowayBindingProp }}</h3>
<h1>Set from child:</h1>
twowayBindingProp: <input ng-model="twowayBindingProp">
</div>
</div>
</div>

Categories