4

With Angular I use specific directive for validate single input field. (i.e. email, username etc.)

In this project I have this scenario:

I need to set an Advance Payment field, this Advance must be between min/max value. To set the Advance I have three input text:

  • Cash
  • Old Car Value
  • Financed Advance

the sum of those three input is my Advance value.

So I need to validate Advance but I have no a single input with ng-model setted on.

What is the best way to check and validate the Advance Payment value?

I need to add a directive to all those three input fields? or is better a directive for the form?

Any suggestion will be appreciated.

jsfiddle example

(function () {
    var app = angular.module("myApp", ['ngMessages']);

    app.controller('myFormCtrl', function($scope) {
        $scope.plan = {};
        $scope.plan.advance = 0;
        $scope.plan.min_advance = 3000;   
        $scope.plan.max_advance = 6000;        

        $scope.updateAdvance = function(){
            var advance = 0;
            if ($scope.quotationAdvanceType && !isNaN($scope.plan.quotation))
                advance += $scope.plan.quotation;
            if ($scope.cashAdvanceType && !isNaN($scope.plan.cash))
                advance += $scope.plan.cash;
            if ($scope.financedAdvanceType && !isNaN($scope.plan.financed))
                advance += $scope.plan.financed;
            $scope.plan.advance = advance;
        }

    });

})();
10
  • If you just need to ensure that all three fields have a value, use required. If you need to do further validation not supported by the builtin directives such as ng-pattern, use a custom directive. You can use ngMessage (or some other directive like ngIf, ngShow/Hide) to create a single error feedback message. Try something and post your code when you run into a problem. Commented Jul 13, 2015 at 9:37
  • Can you be more specific or show some code? Commented Jul 13, 2015 at 9:37
  • i'm going to publish a part of form. Commented Jul 13, 2015 at 9:51
  • @GamerNebulae add a jsfiddle example. I'd like to manage the Advance value as the surname field. The three field concerning Advance are not all required but an Advance Value are mandatory and it need to be between min/max value Commented Jul 13, 2015 at 10:31
  • @Zauker shouldn't you just take the sum of the three fields then and make all of them red if they aren't between the minimum and maximum? Commented Jul 13, 2015 at 10:54

2 Answers 2

2

Just to give you (and perhaps others) another option.

To simplify, you can use a number input with its ng-model set to plan.advance. This will allow you to use the min/max attributes. You can set the input to hidden if you prefer not to display it.

<input type="number" name="advance" ng-model="plan.advance" min="{{plan.min_advance}}" max="{{plan.max_advance}}" hidden />

Then you can use the built in validation for min and max such as:

<span ng-messages="myForm.advance.$error" role="alert">
  <span ng-message="min">Minimum not reached.</span>
  <span ng-message="max">Maximum exceeded.</span>
</span>

Run the code snippet below to see it in action:

(function () {
    var app = angular.module("myApp", ['ngMessages']);

    app.controller('myFormCtrl', function ($scope) {
        var advance = [];
        
        $scope.plan = {};
        $scope.plan.advance = 0;
        $scope.plan.min_advance = 3000;
        $scope.plan.max_advance = 6000;

        $scope.reset = function(val) {
            $scope.plan[val] = undefined;
            $scope.updateAdvance();
        };
        
        $scope.updateAdvance = function () {
            advance = [$scope.plan.quotation, $scope.plan.cash, $scope.plan.financed];
            $scope.plan.advance = advance.reduce(function (a, b) {
                a = a ? a : 0;
                b = b ? b : 0;
                return a + b;
            }, 0);
        }

    });

})();
@import url("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css");
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular-messages.min.js"></script>
<div ng-app="myApp" class="container">
    <form name="myForm" ng-controller="myFormCtrl" class="form-horizontal">
        <div class="form-group" ng-class="{ 'has-error' : myForm.plan.min_advance.$invalid && myForm.plan.min_advance.$touched}">
            <label for="plan.min_advance" class="control-label col-xs-4 col-sm-2">Min Advance</label>
            <div class="col-xs-8 col-sm-10">
                <input type="number" ng-model="plan.min_advance" class="form-control" />
            </div>
        </div>
    
        <div class="form-group" ng-class="{ 'has-error' : myForm.plan.max_advance.$invalid && myForm.plan.max_advance.$touched}">
            <label for="plan.max_advance" class="control-label col-xs-4 col-sm-2">Min Advance</label>
            <div class="col-xs-8 col-sm-10">
                <input type="number" ng-model="plan.max_advance" class="form-control" />
            </div>
        </div>
        <div class="form-group" ng-class="{ 'has-error' : myForm.surname.$invalid && myForm.surname.$touched}">
            <div class="col-xs-12">
                <label for="surname" class="control-label">Surname</label>
                <input type="text" value="" name="surname" id="surname" ng-model="surname" ng-minlength="3" ng-mAXlength="20" required="required" class="form-control" />
                <span ng-messages="myForm.surname.$error" class="help-block" role="alert" ng-show="myForm.surname.$touched">
                    <span ng-message="required">You forgot to enter your surname</span>
                    <span ng-message="minlength">Surname is too short</span>
                    <span ng-message="maxlength">Surname is too long</span>
                </span>
            </div>
        </div>
        <p>Set the Advance between minimun <strong>{{plan.min_advance | currency}}</strong> and maximum <strong>{{plan.max_advance | currency}}</strong>
        </p>
        <div class="form-group">
            <label for="quotation" class="col-xs-4 col-sm-2 control-label">
                <input type="checkbox" name="quotationAdvanceType" ng-model="quotationAdvanceType" ng-change="reset('quotation')"/> Quotation:
            </label>
            <div class="col-xs-8 col-sm-10">
                <input type="number" name="quotation" ng-value="plan.quotation" ng-model="plan.quotation" ng-change="updateAdvance()" class="form-control"  ng-disabled="!quotationAdvanceType" placeholder="{{'max '+ (plan.max_advance-plan.advance)}}" />
            </div>
        </div>
        <div class="form-group">
            <label for="cash" class="col-xs-4 col-sm-2 control-label">
                <input type="checkbox" name="cashAdvanceType" ng-model="cashAdvanceType" ng-change="reset('cash')" /> Cash:
            </label>
            <div class="col-xs-8 col-sm-10">
                <input type="number" name="cash" ng-value="plan.cash" ng-model="plan.cash" ng-change="updateAdvance()" class="form-control" ng-disabled="!cashAdvanceType" placeholder="{{'max '+ (plan.max_advance-plan.advance)}}" />
            </div>
        </div>
        <div class="form-group">
            <label for="financed" class="col-xs-4 col-sm-2 control-label">  
                <input type="checkbox" name="financedAdvanceType" ng-model="financedAdvanceType" ng-change="reset('financed')" /> Financed:
            </label>
            <div class="col-xs-8 col-sm-10">
                <input type="number" name="financed" ng-value="0" ng-model="plan.financed" ng-change="updateAdvance()" class="form-control" ng-disabled="!financedAdvanceType" placeholder="{{'max '+ (plan.max_advance-plan.advance)}}" />
            </div>
        </div>
        <div ng-class="{'has-error' : myForm.advance.$invalid && (quotationAdvanceType || cashAdvanceType || financedAdvanceType) && (myForm.quotation.$dirty || myForm.cash.$dirty || myForm.financed.$dirty)}">
            <input type="number" name="advance" ng-model="plan.advance" min="{{plan.min_advance}}" max="{{plan.max_advance}}" hidden />
            <p class="help-block">Total Advance: <strong>{{plan.advance ? (plan.advance | currency) : 'none'}}</strong>
                <span ng-messages="myForm.advance.$error" role="alert" ng-show="myForm.quotation.$dirty || myForm.cash.$dirty || myForm.financed.$dirty">
                    <span ng-message="min">Minimum not reached.</span>
                    <span ng-message="max">Maximum exceeded.</span>
                </span>
            </p>
        </div>  
        <button type="submit" class="btn btn-primary" ng-disabled="myForm.$invalid">Send</button>
    </form>
</div>

Is there an advantage over your custom directive approach? I would say yes and here's why:

  1. Your directive is tightly coupled to your controller scope, so you can't reuse it elsewhere in your application and it will require you to separately update it if you change one of your scope variables in the future.
  2. You are essentially duplicating code for functionality that already exists in the framework. That results in more upfront cost of development and maintenance effort down the line.
Sign up to request clarification or add additional context in comments.

Comments

1

I solve my question with a brand new directive. I don't know if this is the right way to do that, but it works fine and the behaviour is correct.

app.directive('totalAdvance', function() {
    return {
        restrict: 'E',
        require: '^form',
        link: function(scope, element, attrs, formCtrl) {
            var advanceValue = 0;
            var advanceValidator = function() {
                if (advanceValue >= attrs.min && advanceValue <= attrs.max){
                    formCtrl.$setValidity('minAdvance', true);
                    formCtrl.$setValidity('maxAdvance', true);
                    return advanceValue;
                }else if (advanceValue < attrs.min) {
                    formCtrl.$setValidity('minAdvance', false);
                    formCtrl.$setValidity('maxAdvance', true);
                    return null;
                }else if (advanceValue > attrs.max){
                    formCtrl.$setValidity('minAdvance', true);
                    formCtrl.$setValidity('maxAdvance', false);
                    return null;
                }
            };

            scope.$watch('plan.advance', function(value) {
                advanceValue = value;
                return advanceValidator();
            });
            scope.$watch('plan.min_advance', function() {
                return advanceValidator();
            });
            scope.$watch('plan.max_advance', function() {
                return advanceValidator();
            });

        }
    };
});

jsfiddle example

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.