javascripthtmlangularjsformsangularjs-ng-form

angularjs ng-form and ng-repeat with input validation and submit action


Angular 1.6.2

Try to iterate over elements of collection inputted by user and then check their validity and enable submit action if validation passes.

I try to use ng-form for this purpose and have two approach but each of them has side-effect.

First approach (ng-form at the table level):

<table class="datatable table table-stripped table-responsive" ng-form="form">
    <thead>
    <tr class="headers">
        <th ng-repeat="header in ctrl.headers">{{header}}</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="linkDelay in ctrl.currentLinkDelays">
        <td>{{linkDelay.label}}</td>
        <td>{{linkDelay.value}}</td>
        <td id="proposed-ld-input-column" width=40%>
            <input id="proposed-ld-input" name="input"
                   class="form-control ng-invalid-min ng-invalid-max ng-invalid-required"
                   type="number" ng-model="ctrl.inputs[linkDelay.property]"
                   data-ng-required="true" min="0" max="180"/>
            <div class="error" data-ng-show="form.input.$error.required">
                Message required
            </div>
            <div class="error" data-ng-show="form.input.$error.max">
                Message max
            </div>
            <div class="error" data-ng-show="form.input.$error.min">
                Message min
            </div>
        </td>
    </tr>
    </tbody>
</table>

<button class="btn btn-primary" type="submit" ng-disabled="form.$invalid"
        ng-click="ctrl.applyChanges()">
    <span>Apply</span>
</button>

In this approach submit is disabled until all input are in set ranges but validation messages don't appear.

Second approach (ng-form at the table column level):

<table class="datatable table table-stripped table-responsive">
    <thead>
    <tr class="headers">
        <th ng-repeat="header in ctrl.headers">{{header}}</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="linkDelay in ctrl.currentLinkDelays">
        <td>{{linkDelay.label}}</td>
        <td>{{linkDelay.value}}</td>
        <td id="proposed-ld-input-column" width=40% ng-form="form">
            <input id="proposed-ld-input" name="input"
                   class="form-control ng-invalid-min ng-invalid-max ng-invalid-required"
                   type="number" ng-model="ctrl.inputs[linkDelay.property]"
                   data-ng-required="true" min="0" max="180"/>
            <div class="error" data-ng-show="form.input.$error.required">
                Message required
            </div>
            <div class="error" data-ng-show="form.input.$error.max">
                Message max
            </div>
            <div class="error" data-ng-show="form.input.$error.min">
                Message min
            </div>
        </td>
    </tr>
    </tbody>
</table>

<button class="btn btn-primary" type="submit" ng-disabled="form.$invalid" 
        ng-click="ctrl.applyChanges()">
    <span>Apply</span>
</button>

In this approach validation messages appear but submit button is always enabled.

Could you tell me what I did wrong?


Solution

  • Give the inputs different names:

    <table class="datatable table table-stripped table-responsive" ng-form="form">
        <thead>
        <tr class="headers">
            <th ng-repeat="header in ctrl.headers">{{header}}</th>
        </tr>
        </thead>
        <tbody>
        <tr ng-repeat="linkDelay in ctrl.currentLinkDelays">
            <td>{{linkDelay.label}}</td>
            <td>{{linkDelay.value}}</td>
            <td id="proposed-ld-input-column{{$index}}" width=40%>
                ̶<̶i̶n̶p̶u̶t̶ ̶i̶d̶=̶"̶p̶r̶o̶p̶o̶s̶e̶d̶-̶l̶d̶-̶i̶n̶p̶u̶t̶"̶ ̶n̶a̶m̶e̶=̶"̶i̶n̶p̶u̶t̶"̶
                <input id="proposed-ld-input{{$index}}" name="input{{$index}}"
                       class="form-control ng-invalid-min ng-invalid-max ng-invalid-required"
                       type="number" ng-model="ctrl.inputs[linkDelay.property]"
                       data-ng-required="true" min="0" max="180"/>
                <div class="error" ng-show="form['input'+$index].$error.required">
                    Message required
                </div>
                <div class="error" ng-show="form['input'+$index].$error.max">
                    Message max
                </div>
                <div class="error" ng-show="form['input'+$index].$error.min">
                    Message min
                </div>
            </td>
        </tr>
        </tbody>
    </table>
    
    <button class="btn btn-primary" type="submit" ng-disabled="form.$invalid"
            ng-click="ctrl.applyChanges()">
        <span>Apply</span>
    </button>
    

    The ng-repeat creates multiple inputs. They need to be assigned different names for the error messages to be correct.