Below is some code to clear angular model values when the corresponding input to the model is hidden via ng-show, using classnames and jquery, but it has a bad smell bc it manipulates DOM in controller (Edit- it doesn't manipulate the DOM it changes the scope model values, but i am not crazy about using jquery) . Is there an "angular way" to do this?
I should add that the code below is just for a proof of concept to show that a solution is possible. The actual project has very complicated business rules to show sections, sub-section and sub-sections etc that have many logical branches... so it would be difficult to code that logic in the watch as @New Dev suggests... in addition, I would not want to have the logic in two places: both in all the divs that have show and hide AND in a function ...
<!doctype html>
<html xmlns:ng="http://angularjs.org" ng-app="app">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body ng-controller="MainCtrl">
<div style="padding:20px; background-color:silver;color:blue">{{person | json }}</div>
Name: <input ng-model="person.name" name="name" >
<div ng-show="person.name.length">
Age: <input ng-model="person.age" name="age" class="hide-clear">
<div ng-show="person.age.toString().length">
Hobby: <input ng-model="person.hobby" name="hobby" class="hide-clear">
</div>
</div>
<Script>
angular.module('app', [])
.controller('MainCtrl', function($scope,$log,$timeout){
$scope.person = {
name: 'mr smith',
age: 51,
hobby: 'coding'
}
$scope.$watchCollection(
//return the value to be watched
function($scope){
return $scope.person
},
//function to be called when changed
function(newValue,oldValue){
$timeout( function() {
$(".hide-clear").each(function(){
var t = $(this);
if( ! t.is(":visible") ) {
$scope.person[t.attr('name')] = '';
}
})
})
}
)
})
</Script>
</body>
</html>
I'm glad that you recognized the approach above as a poor design (or "bad smell", as you put it). Indeed, an Angular way (or more generally, an MVVM way) would be to only manipulate the View Model, and let the View Model drive the View.
For example, you are attempting to set $scope.person.age = ""
and $scope.person.hobby = ""
when their parent container is hidden with ng-show="person.name.length"
(i.e. when $scope.person.name
is empty). Instead of using the resulting invisibility of the container as an indicator, use the original data that caused the container to be invisible in the first place.
$scope.$watch("person.name", function(val){
if (val === "") { // or, if (!val.length), to make it completely equivalent
$scope.person.age = "";
$scope.person.hobby = "";
}
});
The code above watches for $scope.person.name
to be empty (and/or undefined
, whatever your definition is) to set the other properties. It doesn't matter at all to the controller how the View reacted to empty person.name
at all - it could have done some animation or other UI tricks. The logic only deals with the View Model state.
The code above could be further improved to avoid a $watch
and instead react to the event that caused $scope.person.name
to become empty. From your example it appears to be only caused by the user deleting the name from the textbox.
<input ng-model="person.name" ng-change="onPersonChanged()">
$scope.onPersonChanged = function(){
if (!$scope.person.name) {
$scope.person.age = "";
$scope.person.hobby = "";
}
};
This is preferable to $watch
since $watch
fires on every digest cycle, whereas ng-change
fires only when there is a change to the input field.