1

This is one of those bugs which has you tearing your hair out.

I have written a directive to perform a custom validation on either a time, distance or points total according to the value chosen in the select box. Here it is working nicely:

http://plnkr.co/edit/FECdILYtahR0HjoWysTr?p=preview

The problem starts when I apply the directive to my application. The form's $invalid property is set to true but the $error.mymeasure, although apparently set to true in the debugger, does not have any effect on the view.

Chrome debugger output screenshot: http://prntscr.com/2peomt

Code to output the $error and $invalid properties:

entriesForm.savedcurvalue.$error.mymeasure: {{ entriesForm.savedcurvalue.$error.mymeasure }} 
  / $invalid: {{ entriesForm.$invalid }} <br/>

Actual output in the view:

entriesForm.savedcurvalue.$error.mymeasure: / $invalid: true

As far as I can tell the relevant code is identical to the test... here's the html: (The input with the directive is at the bottom)

<form name="entriesForm" class="css-form" novalidate>
<div class="grey-border" ng-show="entriesData.currentAthleteIndex != undefined">

<!--********  ALREADY SAVED ********-->
<div ng-show="entriesData.showEventListGroup=='saved'">
    <ul class="inline entry-table-head">
        <li class="entry-col-1">{{ 'entries.class' | i18n }}</li>
        <li class="entry-col-2">{{ 'entries.event.curValue' | i18n }}</li>
        <li class="entry-col-3">{{ 'entries.event.curWeightHeight' | i18n }}</li>
        <li class="entry-col-4">{{ 'entries.event.datDate' | i18n }}</li>
        <li class="entry-col-5">{{ 'entries.event.strTown' | i18n }}</li>
        <li class="entry-col-6">{{ 'entries.event.bolindoor' | i18n }}</li>
    </ul>
    <span ng-show="entriesForm.measurement.$error.measure">{{ measurementFormat }} </span>
    entriesForm.savedcurvalue.$error.mymeasure: {{ entriesForm.savedcurvalue.$error.mymeasure }} / $invalid: {{ entriesForm.$invalid }} <br/>
    <ul class="unstyled">
        <li ng-repeat="event in currentEditItem.athleteList[entriesData.currentAthleteIndex].events"
            ng-show="event.selected && !event.isRelay">

            <ul class="inline">
                <li class="entry-col-1"
                    ng-class="{requestStatusGreen:event.ownEvents && event.ownSex, requestStatusYellow:!event.ownEvents && event.ownSex, requestStatusRed:!event.ownEvents && !event.ownSex}">
                    <label class="checkbox inline">
                        <input type="checkbox" ng-model="event.selected" ng-click="setIntStateSave(event,currentEditItem.athleteList[entriesData.currentAthleteIndex])"/>
                        {{ event.strIdClass }} - {{ event.strName }}
                    </label>
                </li>
                <li class="entry-col-2 control-group" ng-class="{error:entriesForm.saved_curValue.$error.measure}">
                    <input class="input-mini" type="text" ng-model="event.curValue" ng-change="setChecked(event)" name="savedcurvalue"
                           smart-measurement="event.intType" errorformat="measurementFormat" decimal="DECIMAL" />
                </li>

And the directive:

app.directive('smartMeasurement', function () {
    return {
        require: 'ngModel',
        scope: {
            ngModel: '=',
            smartMeasurement: '=',
            errorformat: '=',
            decimal: '='
        },
        link: function (scope, elm, attrs, ctrl) {
            var REGEX;
            var TIME_REGEXP;
            var LENGTH_REGEXP;
            var MULTI_REGEXP = /^(?:\d{1,5})?$/;

            if (scope.decimal == ",") {
                TIME_REGEXP = /^(?:\d+:)?(?:[0-5]\d:|[0-9]:)?(?:[0-5]\d|\d|^\d\d\d)(?:[.,]\d\d?)?$/;
                LENGTH_REGEXP = /^(?:\d{1,3})(?:[,]\d)(\d)?$/;
            } else {
                TIME_REGEXP = /^(?:\d+:)?(?:[0-5]\d:|[0-9]:)?(?:[0-5]\d|\d|^\d\d\d)(?:[.,]\d\d?)?$/;
                LENGTH_REGEXP = /^(?:\d{1,3})(?:[.]\d)(\d)?$/;
            }

            ctrl.$parsers.unshift(function (viewValue) {
                switch (scope.smartMeasurement) {
                    case 3:
                        REGEX = LENGTH_REGEXP;
                        scope.errorformat = "00" + scope.decimal + "00";
                        break;
                    case 4:
                        REGEX = MULTI_REGEXP;
                        scope.errorformat = "00000";
                        break;
                    default:
                        REGEX = TIME_REGEXP;
                        scope.errorformat = "[hh:mm:]ss" + scope.decimal + "t[h] / [s]ss" + scope.decimal + "t[h]";
                        break;
                }
                if (REGEX.test(viewValue)) {
                    ctrl.$setValidity('mymeasure', true);
                    return viewValue;
                } else {
                    ctrl.$setValidity('mymeasure', false);
                    return undefined;
                }
            });
        }
    };
});

I am using Angular 1.06 - The project was started almost a year ago so upgrading to 1.2 is not really an option at the moment. I am thinking that perhaps one on the dependencies could prevent it working. I will test this although I find it unlikely. But that is the only thing I can think of that might be wrong.

If anybody can sport an error or suggest a place I should look, it would be very much appreciated.

1 Answer 1

1

OK, I found the problem. I have the form fields in a loop, which means that the name field is indistinct.

The answer is to add an ng-form at each iteration of the loop and then check the error status of each field of this form...

<form name="form" class="form-horizontal" novalidate>
    form.measurement.$error.measure: {{ form.measurement.$error.measure }} <br/>

    <div class="alert alert-error" ng-show="form.measurement.$error.measure">
        {{ measurementFormat }}
    </div>
    <ul class="unstyled">
        <li class='control-group' ng-class="{error:form.measurement.$error.measure}" ng-repeat="l in loop">
            <ng-form name="loopForm">
                loopForm.measurement.$error.measure: {{ loopForm.measurement.$error.measure }} <br/>
                <input class="input-mini error" type="text" ng-model="measurement" ng-change="setChecked()"
                       name="measurement" smart-measurement=selectedFormat errorformat="measurementFormat" decimal="DECIMAL"/>
                {{measurement}}
            </ng-form>
        </li>
    </ul>
</form>

I hope this helps somebody sometime :-)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.