当字段隐藏在角度中时清除模型值的最佳方法

时间:2015-07-28 18:06:12

标签: angularjs angularjs-directive ng-show ng-hide

下面是一些代码,用于在使用类名和jquery通过ng-show隐藏模型的相应输入时清除角度模型值,但它有一种难闻的气味bc它在控制器中操纵DOM(编辑 - 它没有操纵DOM它改变了范围模型值,但我并不是疯狂使用jquery)。这样做是否有“棱角分明的方式”?

我应该补充一点,下面的代码只是为了证明解决方案是可能的概念验证。实际项目有非常复杂的业务规则来显示具有许多逻辑分支​​的部分,子部分和子部分......所以很难在@New Dev建议的情况下在手表中编写该逻辑......此外,我不想在两个地方都有逻辑:在所有显示和隐藏AND的div中都有... ...

    <!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>

2 个答案:

答案 0 :(得分:4)

我很高兴您认识到上述方法是一种糟糕的设计(或者#34;难闻的气味&#34;,就像你说的那样)。实际上,Angular方式(或更一般地说,MVVM方式)只是操纵View Model,让View Model驱动View。

例如,当您的父容器被$scope.person.age = ""隐藏时(即当$scope.person.hobby = ""为空时),您试图设置ng-show="person.name.length"$scope.person.name。不要使用容器的最终隐形作为指标,而是使用导致容器首先不可见的原始数据。

$scope.$watch("person.name", function(val){
  if (val === "") { // or, if (!val.length), to make it completely equivalent
    $scope.person.age = "";
    $scope.person.hobby = "";
  }
});

上面的代码监视$scope.person.name为空(和/或undefined,无论您的定义是什么)来设置其他属性。对控制器来说,根本不对空的person.name做出反应并不重要 - 它本可以完成一些动画或其他UI技巧。该逻辑仅处理视图模型状态。

可以进一步改进上面的代码以避免$watch,而是对导致$scope.person.name变空的事件作出反应。在您的示例中,它似乎只是由用户从文本框中删除名称引起的。

<input ng-model="person.name" ng-change="onPersonChanged()">
$scope.onPersonChanged = function(){
   if (!$scope.person.name) {
      $scope.person.age = "";
      $scope.person.hobby = "";
   }
};

这比$watch更可取,因为$watch会在每个摘要周期触发,而ng-change只有在输入字段发生变化时才会触发。

答案 1 :(得分:0)

以下是我最好的尝试。我仍然使用jquery来检测元素是否可见,并且该指令不使用隔离范围,但至少所有逻辑都包含在两个指令中,这些指令允许在其他项目中重用:

指令代码(clearmModelWhenHidden.js)

angular.module('clearModelWhenHidden', [])

.directive('clearModelWhenHiddenContainer', function() {

    return {
      scope: false,
      controller: function($scope, $parse, $timeout) {

        $scope.registeredElements = [];

        //since we dont' have an isolate scope, namespace our public API to avoid collision
        this.clearModelWhenHidden = {};

        //to share a method with child directives use the "this" scope and have children require the parent controller... 
        this.clearModelWhenHidden.register = function(e) {
          $scope.registeredElements.push(e);

        }

        $scope.$watchCollection(

          function() {
            //convert the registered elements ng-model attribute from a string to an angular
            //object that can be watched for changes
            var parsedArray = [];
            angular.forEach($scope.registeredElements, function(item, i) {
              parsedArray.push($parse(item.attributes.ngModel)($scope))
            });
            return parsedArray;
          },
          function(newvalue) {

            $timeout(function() {
              angular.forEach($scope.registeredElements, function(item, i) {

                var isVisible = $(item.element).is(':visible');

                if (!isVisible) {

                  var value = $parse(item.attributes.ngModel)($scope);

                  //create a string that sets the ng-model of each element to an empty string,
                  //for example, person.name=''
                  var stringToEval = item.attributes.ngModel + '=""  ';

                  console.log(stringToEval)

                  $parse(stringToEval)($scope);
                }
              })
            });
          }
        );
      }
    }
  })
  .directive('clearModelWhenHidden', function() {
    var link = function(scope, element, attributes, parentController) {
      //pass in the element itself so we can used jquery to detect visibility and the attributes so the container can create a watch on the models
      parentController.clearModelWhenHidden.register({
        'element': element[0],
        'attributes': attributes
      });
    }
    return {
      link: link,
      require: '^clearModelWhenHiddenContainer'
    }
  })

和演示页

<!doctype html>
<html xmlns:ng="http://angularjs.org" ng-app="app">

<head>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js" type="text/javascript"></script>
  <script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>
  <script language="javascript" src="clearModelWhenHidden.js"></script>
</head>

<body ng-controller="MainCtrl as MainCtrl">

  <div style="padding:20px; background-color:silver;color:blue">{{MainCtrl.person | json }}</div>

  <div clear-model-when-hidden-container>

    <section>

      Name:
      <input ng-model="MainCtrl.person.name" clear-model-when-hidden>

      <div ng-show="MainCtrl.person.name.length">
        <label>Age</label>:
        <input ng-model="MainCtrl.person.age" clear-model-when-hidden>

        <section ng-if="MainCtrl.person.age.toString().length">
          <label>Hobby</label>:
          <input ng-model="MainCtrl.person.hobby" clear-model-when-hidden>
        </section>
      </div>
    </section>
  </div>

</body>

</html>