diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 2677921c0d74..fe03a7af4a34 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -4,6 +4,7 @@ */ var nullFormCtrl = { $addControl: noop, + $getControls: valueFn([]), $$renameControl: nullFormRenameControl, $removeControl: noop, $setValidity: noop, @@ -159,6 +160,30 @@ FormController.prototype = { control.$$parentForm = this; }, + /** + * @ngdoc method + * @name form.FormController#$getControls + * @returns {Array} the controls that are currently part of this form + * + * @description + * This method returns a **shallow copy** of the controls that are currently part of this form. + * The controls can be instances of {@link form.FormController `FormController`} + * ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}. + * If you need access to the controls of child-forms, you have to call `$getControls()` + * recursively on them. + * This can be used for example to iterate over all controls to validate them. + * + * The controls can be accessed normally, but adding to, or removing controls from the array has + * no effect on the form. Instead, use {@link form.FormController#$addControl `$addControl()`} and + * {@link form.FormController#$removeControl `$removeControl()`} for this use-case. + * Likewise, adding a control to, or removing a control from the form is not reflected + * in the shallow copy. That means you should get a fresh copy from `$getControls()` every time + * you need access to the controls. + */ + $getControls: function() { + return shallowCopy(this.$$controls); + }, + // Private API: rename a form control $$renameControl: function(control, newName) { var oldName = control.$name; diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index 2d996bb359e1..42044dd207f4 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -1200,6 +1200,52 @@ describe('form', function() { }); }); + describe('$getControls', function() { + it('should return an empty array if the controller has no controls', function() { + doc = $compile('
')(scope); + + scope.$digest(); + + var formCtrl = scope.testForm; + + expect(formCtrl.$getControls()).toEqual([]); + }); + + it('should return a shallow copy of the form controls', function() { + doc = $compile( + '
' + + '' + + '
' + + '' + + '
' + + '
')(scope); + + scope.$digest(); + + var form = doc, + formCtrl = scope.testForm, + formInput = form.children('input').eq(0), + formInputCtrl = formInput.controller('ngModel'), + nestedForm = form.find('div'), + nestedFormCtrl = nestedForm.controller('form'), + nestedInput = nestedForm.children('input').eq(0), + nestedInputCtrl = nestedInput.controller('ngModel'); + + var controls = formCtrl.$getControls(); + + expect(controls).not.toBe(formCtrl.$$controls); + + controls.push('something'); + expect(formCtrl.$$controls).not.toContain('something'); + + expect(controls[0]).toBe(formInputCtrl); + expect(controls[1]).toBe(nestedFormCtrl); + + var nestedControls = controls[1].$getControls(); + + expect(nestedControls[0]).toBe(nestedInputCtrl); + }); + }); it('should rename nested form controls when interpolated name changes', function() { scope.idA = 'A';