2013-09-08 21 views
27

Powiedzmy mam następujący (bardzo prosty) strukturę danych:kątowe: Weryfikacja wiele pól zależnych

$scope.accounts = [{ 
    percent: 30, 
    name: "Checking"}, 
{ percent: 70, 
    name: "Savings"}]; 

Potem mają następującą strukturę jako część postaci:

<div ng-repeat="account in accounts"> 
    <input type="number" max="100" min="0" ng-model="account.percent" /> 
    <input type="text" ng-model="account.name" /> 
</div> 

Teraz Chcę potwierdzić, że suma procentowa wynosi 100 dla każdego zestawu kont, ale większość przykładów, które widziałem w przypadku dyrektyw niestandardowych, dotyczy wyłącznie sprawdzania wartości indywidualnej. Jaki jest idiomatyczny sposób tworzenia dyrektywy, która może sprawdzać wiele zależnych pól jednocześnie? Istnieje wiele rozwiązań do tego w jquery, ale nie byłem w stanie znaleźć dobrego źródła dla Angular.

EDYCJA: Mam do czynienia z następującą dyrektywą niestandardową ("udział" to synonim "procentu" oryginalnego kodu). Dyrektywa zatwierdzania udziału przyjmuje jako wartość mapę o numerze "{group: accounts, id: $index}".

app.directive('shareValidate', function() { 
return { 
    restrict: 'A', 
    require: 'ngModel', 
    link: function(scope, elem, attr, ctrl) { 
     ctrl.$parsers.unshift(function(viewValue) { 
      params = angular.copy(scope.$eval(attr.shareValidate)); 
      params.group.splice(params.id, 1); 
      var sum = +viewValue; 
      angular.forEach(params.group, function(entity, index) { 
       sum += +(entity.share); 
      }); 
      ctrl.$setValidity('share', sum === 100); 
      return viewValue; 
     }); 
    } 
}; 
}); 

to prawie działa, ale nie może obsłużyć przypadek, w którym pole jest unieważniony, ale późniejsza zmiana w innej dziedzinie sprawia, że ​​kolejny ważny. Na przykład:

Field 1: 61 
Field 2: 52 

Jeśli wezmę Pole 2 do 39, Pole 2 będzie teraz ważne, ale Pole 1 jest nadal nieprawidłowa. Pomysły?

Odpowiedz

14

Ok, następujące prace (znowu, „akcja” jest „procent”):

app.directive('shareValidate', function() { 
return { 
    restrict: 'A', 
    require: 'ngModel', 
    link: function(scope, elem, attr, ctrl) { 
     scope.$watch(attr.shareValidate, function(newArr, oldArr) { 
      var sum = 0; 
      angular.forEach(newArr, function(entity, i) { 
       sum += entity.share; 
      }); 
      if (sum === 100) { 
       ctrl.$setValidity('share', true); 
       scope.path.offers.invalidShares = false; 
      } 
      else { 
       ctrl.$setValidity('share', false); 
       scope.path.offers.invalidShares = true; 
      } 
     }, true); //enable deep dirty checking 
    } 
}; 
}); 

W HTML, należy ustawić atrybut jako „akcji validate”, a wartość do zbioru obiektów chcesz obejrzeć.

+5

Przyjemnie działające rozwiązanie, ale powiedziałbym, że można go ulepszyć, aby było bardziej ogólne. Nie uważam, że dobrą praktyką jest kodowanie ścieżek zakresu wewnątrz dyrektywy. Zrobiłem coś bardziej ogólnego (może ci to pomoże w przyszłości): http://liviutrifoi.wordpress.com/2013/10/19/angular-custom-validation-with-business-rules/ –

+1

Witam, czy możesz podzielić się działające skrzypce dla powyższego przykładu, Dzięki – Vamsikrishna

8

Możesz sprawdzić bibliotekę angularui (część ui-utility). Posiada dyrektywę ui-validate.

Jeden sposób można wdrożyć go następnie jest

<input type="number" name="accountNo" ng-model="account.percent" 
ui-validate="{overflow : 'checkOverflow($value,account)' }"> 

Na kontrolerze stworzyć metodę checkOverflow że zwróci true false na podstawie obliczeń konta.

Nie próbowałem tego sam, ale chcę podzielić się pomysłem. Przeczytaj również próbki obecne na stronie.

+1

OK, sprawdzę to. Interesuje mnie również, jak zbudować własną dyrektywę, aby rozwiązać ten problem. – HaskellMan

+0

Jeśli tworzysz własną dyrektywę, możesz zajrzeć do sekcji "Sprawdzanie niestandardowe" w przewodniku dla programistów http://docs.angularjs.org/guide/forms – Chandermani

+12

Nie sądzę, że funkcja sprawdzania ui działa tutaj, ponieważ sprawdza również tylko edytowane pole. Tak więc, jeśli wpiszesz np. 20 w obu polach, oba są nieważne. Jeśli zmienisz jedną z nich na 80, to pole stanie się ważne, ale inne pole będzie nadal nieprawidłowe. –

3

Mam przypadek, w którym mam dynamiczną formę, gdzie mogę mieć zmienną liczbę pól wejściowych w moim formularzu i musiałem ograniczyć liczbę dodawanych kontrolek wejściowych.

Nie mogłem łatwo ograniczyć dodawania tych pól wejściowych, ponieważ zostały wygenerowane przez kombinację innych czynników, więc musiałem unieważnić formularz, jeśli liczba pól wejściowych przekroczyła limit. Zrobiłem to, tworząc odniesienie do formularza w moim kontrolerze ctrl.myForm, a następnie za każdym razem, gdy formanty wejściowe są generowane dynamicznie (w moim kodzie kontrolera), wykonywałbym kontrolę limitu, a następnie ustawiałem ważność w formularzu tak: ctrl.myForm.$setValidity("maxCount", false);

To działało dobrze, ponieważ sprawdzanie poprawności nie zostało określone przez określone pole wejściowe, ale ogólną liczbę moich danych wejściowych. To samo podejście może zadziałać, jeśli masz sprawdzanie poprawności, które należy wykonać, które jest określone przez połączenie wielu pól.

0

Dla mojej poczytalności

HTML

<form ng-submit="applyDefaultDays()" name="daysForm" ng-controller="DaysCtrl"> 
<div class="form-group"> 
    <label for="startDate">Start Date</label> 
    <div class="input-group"> 
     <input id="startDate" 
       ng-change="runAllValidators()" 
       ng-model="startDate" 
       type="text" 
       class="form-control" 
       name="startDate" 
       placeholder="mm/dd/yyyy" 
       ng-required 
     /> 
    </div> 
</div> 
<div class="form-group"> 
    <label for="eEndDate">End Date</label> 
    <div class="input-group"> 
     <input id="endDate" 
       ng-change="runAllValidators()" 
       ng-model="endDate" 
       type="text" 
       class="form-control" 
       name="endDate" 
       placeholder="mm/dd/yyyy" 
       ng-required 
     /> 
    </div> 
</div> 
<div class="text-right"> 
    <button ng-disabled="daysForm.$invalid" type="submit" class="btn btn-default">Apply Default Dates</button> 
</div> 

JS

'use strict'; 

angular.module('myModule') 
    .controller('DaysCtrl', function($scope, $timeout) { 
    $scope.initDate = new Date(); 
    $scope.startDate = angular.copy($scope.initDate); 
    $scope.endDate = angular.copy($scope.startDate); 
    $scope.endDate.setTime($scope.endDate.getTime() + 6*24*60*60*1000); 

    $scope.$watch("daysForm", function(){ 
     //fields are only populated after controller is initialized 
     $timeout(function(){ 
     //not all viewalues are set yet for somereason, timeout needed 
     $scope.daysForm.startDate.$validators.checkAgainst = function(){ 
      $scope.daysForm.startDate.$setDirty(); 
      return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <= 
      (new Date($scope.daysForm.endDate.$viewValue)).getTime(); 
     }; 

     $scope.daysForm.endDate.$validators.checkAgainst = function(){ 
      $scope.daysForm.endDate.$setDirty(); 
      return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <= 
      (new Date($scope.daysForm.endDate.$viewValue)).getTime(); 
     }; 
     }); 
    }); 

    $scope.runAllValidators = function(){ 
     //need to run all validators on change 
     $scope.daysForm.startDate.$validate(); 
     $scope.daysForm.endDate.$validate(); 
    }; 

    $scope.applyDefaultDays = function(){ 
     //do stuff 
    } 
    }); 
0

Można zdefiniować jedną dyrektywę, która jest odpowiedzialna tylko dla tej kontroli.

<form> 
    <div ng-repeat="account in accounts"> 
    <input type="number" max="100" min="0" ng-model="account.percent" /> 
    <input type="text" ng-model="account.name" /> 
    </div> 
    <!-- HERE IT IS --> 
    <sum-up-to-hundred accounts="accounts"></sum-up-to-hundred> 
</form> 

A oto prosty kod dyrektywy.

app.directive('sumUpToHundred', function() { 
    return { 
    scope: { 
     accounts: '<' 
    }, 
    require: { 
     formCtrl: '^form' 
    }, 
    bindToController: true, 
    controllerAs: '$ctrl', 
    controller: function() { 
     var vm = this; 

     vm.$doCheck = function(changes) { 
     var sum = vm.accounts.map((a)=> a.percent).reduce((total, n)=> total + n); 
     if (sum !== 100) { 
      vm.formCtrl.$setValidity('sumuptohundred', false); 
     } else { 
      vm.formCtrl.$setValidity('sumuptohundred', true); 
     } 
     }; 
    } 
    }; 
}); 

Here - jest plunker.

Powiązane problemy