手动删除ng-repeat集合中的项目后,ng-form不可用

时间:2014-12-26 14:46:52

标签: angularjs angularjs-ng-repeat angularjs-ng-form

修改以下是此问题的plunk

我有一个会计交易的主要详细信息表格。主部分只包含两个字段名称和类型。细节部分可以有两个或更多条目,每个条目都有AccountId,Debit和Credit字段。表格看起来像是 enter image description here
您可以看到每个条目都有一个删除按钮,因此如果我们有两个以上的条目,我们可以随机删除任何条目。表单html如下所示

<body data-ng-app="transactions">
    <div data-ng-controller="transactionsController">
        <form role="form" name="transactionForm" novalidate data-ng-submit="create()">
            <div class="row">
                <div class="col-md-2">
                    &nbsp;
                </div>
                <div class="col-md-4">
                    <h2 class="form-login-heading">Create Transaction</h2>
                    <div data-ng-repeat="error in errors" class="alert alert-danger">
                        {{error[0]}}
                    </div>
                    <input type="text" name="name" class="form-control" placeholder="Name" data-ng-model="transaction.Name" required autofocus>
                    <div>
                        <span class="error" data-ng-show="transactionForm.name.$error.required && submitted">Please enter Name</span>
                    </div>
                    <input type="text" name="type" class="form-control" placeholder="Type" data-ng-model="transaction.Type" required>
                    <div>
                        <span class="error" data-ng-show="transactionForm.type.$error.required && submitted">Please enter Type</span>
                    </div>
                    <!--<input type="text" readonly name="number" class="form-control" placeholder="Number" data-ng-model="transaction.Number">
                    <input type="checkbox" data-ng-model="transaction.IsFinalized" /> <label>Finalize</label>-->


                    <table>
                        <tr>
                            <th>Account</th>
                            <th>Debit</th>
                            <th>Credit</th>
                            <th>&nbsp;</th>
                        </tr>
                        <tr data-ng-form="entryForm" data-ng-repeat="entry in transaction.Entries track by $index">
                            <td>
                                <input required name="accountId" data-ng-model="entry.AccountId" class="form-control" />
                                <span class="error" data-ng-show="entryForm.accountId.$error.required && submitted">Please select an account</span>
                            </td>
                            <td>
                                <input type="text" name="debit" data-ng-required="!entry.CreditAmount" class="form-control" placeholder="Debit" data-ng-model="entry.DebitAmount">
                                <span class="error" data-ng-show="entryForm.debit.$error.required && submitted">Debit is required</span>
                            </td>
                            <td>
                                <input type="text" data-ng-focus="checkAddRow($index)" name="credit" data-ng-required="!entry.DebitAmount" class="form-control" placeholder="Credit" data-ng-model="entry.CreditAmount">
                                <span class="error" data-ng-show="entryForm.credit.$error.required && submitted">Credit is required</span>
                            </td>
                            <td><button data-ng-show="transaction.Entries.length>2" class="btn btn-md btn-info " type="button" data-ng-click="deleteRow($index)">delete</button></td>




                        </tr>
                        <tr>
                            <td>Total</td>
                            <td><input readonly name="totalDebit" type="text" class="form-control" placeholder="Total Debit" data-ng-value="totalDebit()"></td>
                            <td><input readonly name="totalCredit" compare-to="totalDebit" type="text" class="form-control" placeholder="Total Credit" data-ng-value="totalCredit()"></td>
                        </tr>
                        <tr>
                            <td></td>
                            <td><b>Difference</b></td>
                            <td>
                                <input name="difference" readonly type="text" class="form-control" data-ng-value="difference()">
                                <!--<span class="error" data-ng-show="submitted && !differencezero">Difference should be 0</span>-->
                            </td>
                        </tr>
                    </table><br />
                    <button class="btn btn-md btn-info" type="submit">Create</button>
                    <button class="btn btn-md btn-info" data-ng-show="transaction.Entries.length<15" type="button" data-ng-click="addRow()">Add Row</button>
                    <div data-ng-hide="message == ''" class="alert alert-danger">
                        {{message}}
                    </div>
                </div>
                <div class="col-md-4">

                </div>
                <div class="col-md-2">
                    &nbsp;
                </div>
            </div>
        </form>
        <style type="text/css">
            .error {
                color: red;
            }
        </style>

        <pre>{{transactionForm.entryForm|json}}</pre>
    </div>

我要求当焦点在最后一个条目的Credit输入时,新条目应该自动添加到UI。我是通过在我的控制器上使用addRowcheckAddRow方法来实现的。这些方法如下

$scope.checkAddRow = function (index) {
                if (index == $scope.transaction.Entries.length - 1) {
                    $scope.addRow();
                }
            }

            $scope.addRow = function () {
                entry = {
                    EntryTime: '',
                    DebitAmount: '',
                    CreditAmount: '',
                    AccountId: ''
                };
                $scope.transaction.Entries.push(entry);
                console.log($scope.transactionForm);
            }

            $scope.deleteRow = function (index) {
                $scope.transaction.Entries.splice(index, 1);
                console.log($scope.transactionForm);
            }

此部分再次很好并且效果很好。但我有另一个要求,即如果不使用最后一个条目,它不应该导致表单无效。它应该从transaction.Entries集合中删除,其余数据应该正常保存。为实现这一目标,我在create上定义了$scope函数,如下所示

$scope.create = function () {
                $scope.submitted = true;
                if ($scope.transactionForm.entryForm && $scope.transactionForm.entryForm.$invalid && $scope.transactionForm.entryForm.$pristine) {

                    $timeout(function () {
                        $scope.deleteRow($scope.transaction.Entries.length - 1);
                    });

                    $timeout(function () {
                        console.log('From time out', $scope.transactionForm.$valid);
                        console.log($scope.transactionForm.$valid);
                        if (!$scope.transactionForm.$valid) return;

                        alert('data saved!');
                        console.log($scope.transactionForm);
                        //$scope.transactionForm.name.focus();
                    }, 200);

                }
                else {
                    if ($scope.transactionForm.$valid) {
                        alert('data saved 2');
                    }
                }

            }

您可以看到create函数正在执行的操作。正在检查主表单(entryForm (ng-form))中是否存在transactionForm,然后检查entryForm$invalid还是$pristine如果所有这些标志都为真则,我删除$scope.transaction.Entries中的最后一个条目并在$timeout之后保存数据(目前是保存节目的警报)。如果我没有使用超时,那么表单无效,所以我必须等待200ms才能在删除最后一行后检查表单$valid标志。但令我惊讶的是,当我从create function中删除最后一行时,外部entryForm没有附加transactionForm。另一方面,如果我使用UI上的删除按钮删除条目,则entryForm位于主transactionForm内。谁能解释为什么会这样。我在最后添加<pre>{{transactionForm|json}}</pre>以查看它何时以及何时在主表单上不可用。我创建了一个plunk来表明我的意思。只需在主部分的两个输入字段中添加一些数据,在两个条目的accountid字段中输入一些数据,当您到达第二个(最后)条目的Credit输入时,将自动添加一个新条目。忽略该行,然后按下“创建”按钮。最后一个条目将被删除,数据将保存,但entryForm将不再存在。我不确定我做错了什么。

1 个答案:

答案 0 :(得分:1)

所以,这里的一个问题是你对表单是否有效的定义取决于最后一行的状态。

最后一行可能有以下几种:

  1. 从后端获取行,但不是新行 - &gt;应该只在无效时失效
  2. 行是新的,$ pristine - &gt;
  3. 不应该无效
  4. 行是新的,但是$ dirty(还是最后一行) - &gt;应该只在无效时失效
  5. 您正尝试删除最后一行,然后重新评估表单的有效性。

    另一种方法 - 如果表格处于$pristine状态,请不要让最后一行无效:

    这是一个简化的例子:

    <form name="transactionForm" ng-submit="submit()" novalidate>
      <table>    
        <tr ng-form="entryForm" ng-repeat="transaction in transactions">
    
          <td><input ng-model="transaction.account" 
                     ng-required="transaction !== newLastEntry || entryForm.$dirty"></td>
    
          <td><input ng-model="transaction.amount" 
                     ng-required="transaction !== newLastEntry || entryForm.$dirty"
                     ng-focus="addEntryIfLast($last)" type="number"></td>
        </tr>
      </table>
    </form>
    

    注意$scope.newLastEntry。它被设置为新的空(和最后)条目。添加新的空行时会发生这种情况:

    function addEmptyEntry(){
      $scope.newLastEntry = {};
      $scope.transactions.push($scope.newLastEntry);
    }
    

    因此,ng-required仅在行不是新的时候应用,否则$dirty

    然后,在提交时,您可以删除最后一个条目,如果它处于$pristine状态,实际上是新的条目(而不是之前存在的条目):

    $scope.submit = function(){
      var itemsToSubmit = angular.copy($scope.transactions);
    
      if ($scope.transactionForm.$invalid) return;
    
      if ($scope.transactionForm.entryForm && 
          $scope.transactionForm.entryForm.$pristine && 
          $scope.transactions[$scope.transactions.length - 1] === $scope.newLastEntry) {
    
        itemsToSubmit.splice(itemsToSubmit.length - 1);
      }
      console.log(JSON.stringify(itemsToSubmit));
    };
    

    plunker