Получение элементов управления формой из FormController

Мне нужен способ перебора зарегистрированных элементов управления формы AngularJS. По сути, я пытаюсь получить все элементы управления $dirty, но массива элементов управления нет (у FormController есть ряд различных свойств/функций в дополнение к содержанию самих элементов управления - каждый как собственный объект).

Я просматривал исходный код и вижу, что в FormController есть массив controls, который является именно тем массивом, который я ищу. Есть ли способ получить доступ к этому значению или расширить FormController, включив в него функцию, которая возвращает этот массив controls?

Изменить: Plnkr демо

Кроме того, я понял, что технически я могу проверить первый символ в ключевой строке на «$», но я хотел бы избежать этого в случае изменения FormController/директивы в будущей версии Angular.

Редактировать 2: Еще одно уточнение: моя цель во всем этом - определить, какие конкретные поля являются $ грязными, будь то путем перебора всего списка элементов управления (не включая $ грязный, $ недействительный, $ ошибка, $ имя, и другие свойства, которые живут в объекте формы как есть) или путем расширения FormController и создания функции, которая возвращает только те элементы управления, которые в настоящее время являются грязными (и не равны их начальным значениям).

Редактировать 3: решение, которое я ищу, должно быть применимо к формам/моделям различных структур. Модели в области генерируются с помощью AJAX, поэтому их структура уже установлена ​​(я хотел бы избежать необходимости жесткого кодирования новой структуры для всех данных, которые я уже получаю через AJAX). Кроме того, я хочу использовать этот процесс отправки формы в нескольких формах/моделях, и каждая из них имеет разные структуры JSON, поскольку они применяются к разным объектам в нашей объектной модели. Вот почему я решил спросить способ получить доступ к объекту controls в FormController (я опубликую код из FormController ниже), потому что это единственное место, где я могу получить плоский массив всех моих поля.

function FormController(element, attrs) {


var form = this,
      parentForm = element.parent().controller('form') || nullFormCtrl,
      invalidCount = 0, // used to easily determine if we are valid
      errors = form.$error = {},
      controls = [];

  // init state
  form.$name = attrs.name || attrs.ngForm;
  form.$dirty = false;
  form.$pristine = true;
  form.$valid = true;
  form.$invalid = false;

  parentForm.$addControl(form);

  // Setup initial state of the control
  element.addClass(PRISTINE_CLASS);
  toggleValidCss(true);

  // convenience method for easy toggling of classes
  function toggleValidCss(isValid, validationErrorKey) {
    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
    element.
      removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
      addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
  }

  /**
   * @ngdoc function
   * @name ng.directive:form.FormController#$addControl
   * @methodOf ng.directive:form.FormController
   *
   * @description
   * Register a control with the form.
   *
   * Input elements using ngModelController do this automatically when they are linked.
   */
  form.$addControl = function(control) {
    controls.push(control);

    if (control.$name && !form.hasOwnProperty(control.$name)) {
      form[control.$name] = control;
    }
  };

  /**
   * @ngdoc function
   * @name ng.directive:form.FormController#$removeControl
   * @methodOf ng.directive:form.FormController
   *
   * @description
   * Deregister a control from the form.
   *
   * Input elements using ngModelController do this automatically when they are destroyed.
   */
  form.$removeControl = function(control) {
    if (control.$name && form[control.$name] === control) {
      delete form[control.$name];
    }
    forEach(errors, function(queue, validationToken) {
      form.$setValidity(validationToken, true, control);
    });

    arrayRemove(controls, control);
  };

  // Removed extra code
}

Как видите, сама форма имеет массив controls в частном порядке. Мне интересно, есть ли способ расширить FormController, чтобы я мог сделать этот объект общедоступным? Или создать общедоступную функцию, чтобы я мог хотя бы просмотреть частный массив?


person robert.bo.roth    schedule 03.01.2014    source источник
comment
У вас нет моделей, связанных с элементами управления? Вы можете просто наблюдать за моделями, чтобы увидеть, изменились ли они, а не зацикливаться на элементах управления формы.   -  person m.e.conroy    schedule 03.01.2014
comment
У меня есть модели, я просто также стараюсь не делать часы, потому что в директиве Form уже есть часы для обработки всей грязной проверки, а для больших форм это может выполняться медленно :(   -  person robert.bo.roth    schedule 03.01.2014
comment
Хороший вопрос, это то же самое, что я ищу (моя цель - остановить проверку при отправке после появления первой ошибки). За это время вы нашли альтернативное решение, лучшее, чем проверка первого символа ($)?   -  person Fernando    schedule 01.04.2014
comment
К сожалению, я все еще использую это для проверки. Если я найду лучший способ, я обновлю пост.   -  person robert.bo.roth    schedule 02.04.2014


Ответы (4)


Для прямого решения вопроса измените ответ @lombardo следующим образом;

     var dirtyFormControls = [];
     var myForm = $scope.myForm;
     angular.forEach(myForm, function(value, key) {
         if (typeof value === 'object' && value.hasOwnProperty('$modelValue') && value.$dirty)
             dirtyFormControls.push(value)                        
     });

Затем массив 'dirtyFormControls' будет содержать грязные элементы управления формы.

Вы также можете использовать этот трюк для отображения сообщений об ошибках при отправке формы для «Обязательных» проверок и всех других. В вашей функции submit() вы сделаете что-то вроде;

 if (form.$invalid) {
     form.$setDirty();              
     angular.forEach(form, function(value, key) {
         if (typeof value === 'object' && value.hasOwnProperty('$modelValue'))
             value.$setDirty();                        
     });
    //show user error summary at top of form.
     $('html, body').animate({
         scrollTop: $("#myForm").offset().top
     }, 1000);
     return;
 }

И в вашей форме вы будете показывать сообщения об ошибках с

    <span ng-messages="myForm['subject-' + $index].$error" ng-show="myForm['subject-' + $index].$dirty" class="has-error">
        <span ng-message="required">Course subject is required.</span>
    </span>

Приведенное выше решение полезно, когда у вас есть динамически генерируемые элементы управления, использующие «ng-repeat» или что-то подобное.

person faitha    schedule 06.12.2015
comment
Я совершенно забыл, что отправил этот вопрос, и в итоге сделал что-то очень похожее на то, что вы разместили здесь. На самом деле я расширил директиву ngSubmit, чтобы она запускала весь мой код проверки для ВСЕХ моих элементов ‹form›, которые имеют функции ngSubmit. - person robert.bo.roth; 07.12.2015
comment
Я надеюсь, что решил вашу проблему, не мешая вам! Использование директив — это, вероятно, лучший способ делать пользовательские вещи в AngularJS, но есть много встроенных вещей, которыми можно воспользоваться. - person faitha; 09.12.2015
comment
Просто позаботьтесь о ng-form. Этот метод их не обрабатывает. - person user1067920; 05.07.2016
comment
Я считаю, что это решение работает только для элементов управления, у которых установлен атрибут имени. - person jrharshath; 21.07.2017

Вы можете использовать следующий код для итерации элементов управления:

    var data = {};
    angular.forEach(myForm, function (value, key) {
        if (value.hasOwnProperty('$modelValue'))
            data[key] = value.$modelValue;
    });
person lombardo    schedule 20.04.2014
comment
Мне пришлось добавить typeof value === 'object' && к вашему условию if. В противном случае вы можете получить такие ошибки, как TypeError: Cannot read property 'hasOwnProperty' of undefined - person Bryan Larsen; 25.09.2014
comment
@BryanLarsen Для этого лучше использовать angular.isObject(value). Он также проверяет нулевые значения, поскольку typeof null также является объектом. - person Daniel Z.; 17.12.2016

попробуйте просто изнутри вашего контроллера:

$scope.checkForm = function(myFormName){
     console.log(myFormName.$invalid);
}

ОБНОВИТЬ:

<div ng-controller="MyController">
                <form name="form" class="css-form" novalidate>
                    <input type="text" ng-model="user.uname" name="uname" required /><br />
                    <input type="text" ng-model="user.usurname" name="usurname" required /><br />
                    <button ng-click="update(form)">SAVE</button>
                </form>
              </div>

app.controller('MyController',function($scope){
                $scope.user = {};
                $scope.update = function (form){
                    var editedFields = [];
                    angular.forEach($scope.user, function(value, key){
                        if(form[key].$dirty){
                           this.push(key + ': ' + value); 
                        }

                    }, editedFields);
                    console.log(editedFields);
                }
        });
person Whisher    schedule 03.01.2014
comment
Я не думаю, что мой вопрос был ясен, поэтому я добавил демонстрацию plnkr. У меня нет проблем с определением, является ли форма $ грязной или $ недействительной. Моя проблема в том, что у меня нет хорошего способа просмотреть все элементы управления формы (сами поля), чтобы определить, какие конкретные поля являются $ грязными. Моя основная цель - отправить только те поля, которые были отредактированы. - person robert.bo.roth; 03.01.2014
comment
Это в значительной степени то, что у меня есть на данный момент, но моя проблема в том, что мне приходится перебирать неструктурированный/многомерный объект JSON в моей области, поэтому я бы предпочел иметь возможность перебирать плоский массив полей, который находится в FormController - person robert.bo.roth; 03.01.2014

Я нашел довольно приличное решение, но оно все равно не то, что я искал. Я спас некоторый код от другой проблемы, связанной с созданием объектов JSON из строк, и придумал следующее:

По сути, я называю свои поля так же, как они привязаны к модели, а затем создаю новый объект для отправки при вызове form_submit.

демонстрация Plnkr

В демонстрации, если вы измените любое из полей формы, а затем нажмете «Отправить», вы увидите всплывающий объект только с грязными значениями.

person robert.bo.roth    schedule 03.01.2014