AngularJs can't access form object in controller ($scope)

Angularjs

Angularjs Problem Overview


I am using bootstrap-ui more specifically modal windows. And I have a form in a modal, what I want is to instantiate form validation object. So basically I am doing this:

<form name="form">
	<div class="form-group">
		<label for="answer_rows">Answer rows:</label>
		<textarea name="answer_rows" ng-model="question.answer_rows"></textarea>
	</div>
</form>

<pre>
	{{form | json}}
</pre

I can see form object in the html file without no problem, however if I want to access the form validation object from controller. It just outputs me empty object. Here is controller example:

.controller('EditQuestionCtrl', function ($scope, $modalInstance) {
	$scope.question = {};
	$scope.form = {};

	$scope.update = function () {
		console.log($scope.form); //empty object
		console.log($scope.question); // can see form input
	};
});

What might be the reasons that I can't access $scope.form from controller ?

Angularjs Solutions


Solution 1 - Angularjs

Just for those who are not using $scope, but rather this, in their controller, you'll have to add the controller alias preceding the name of the form. For example:

<div ng-controller="ClientsController as clients">
  <form name="clients.something">
  </form>
</div>

and then on the controller:

app.controller('ClientsController', function() {
  // setting $setPristine()
  this.something.$setPristine();
};

Hope it also contributes to the overall set of answers.

Solution 2 - Angularjs

The normal way if ng-controller is a parent of the form element: please remove this line:

$scope.form = {};

If angular sets the form to your controllers $scope you overwrite it with an empty object.


As the OP stated that is not the case here. He is using $modal.open, so the controller is not the parent of the form. I don't know a nice solution. But this problem can be hacked:

<form name="form" ng-init="setFormScope(this)">
...

and in your controller:

$scope.setFormScope= function(scope){
   this.formScope = scope;
}

and later in your update function:

$scope.update = function () {
    console.log(this.formScope.form); 

};

Solution 3 - Angularjs

Look at the source code of the 'modal' of angular ui bootstrap, you will see the directive has

transclude: true

This means the modal window will create a new child scope whose parent here is the controller $scope, as the sibling of the directive scope. Then the 'form' can only be access by the newly created child scope.

One solution is define a var in the controller scope like

$scope.forms = {};

Then for the form name, we use something like forms.formName1. This way we could still access it from our controller by just call $scope.forms.formName1.

This works because the inheritance mechanism in JS is prototype chain. When child scope tries to create the forms.formName1, it first tries to find the forms object in its own scope which definitely does not have it since it is created on the fly. Then it will try to find it from the parent(up to the prototype chain) and here since we have it defined in the controller scope, it uses this 'forms' object we created to define the variable formName1. As a result we could still use it in our controller to do our stuff like:

if($scope.forms.formName1.$valid){
      //if form is valid
}

More about transclusion, look at the below Misco's video from 45 min. (this is probably the most accurate explanation of what transcluded scopes are that I've ever found !!!)

www.youtube.com/watch?v=WqmeI5fZcho

Solution 4 - Angularjs

No need for the ng-init trickery, because the issue is that $scope.form is not set when the controller code is run. Remove the form = {} initialization and get access to the form using a watch:

$scope.$watch('form', function(form) {
  ...
});

Solution 5 - Angularjs

I use the documented approach.

https://docs.angularjs.org/guide/forms

so, user the form name, on "save" click for example just pass the formName as a parameter and hey presto form available in save method (where formScopeObject is greated based upon the ng-models specifications you set in your form OR if you are editing this would be the object storing the item being edited i.e. a user account)

<form name="formExample" novalidate>

<!-- some form stuff here -->
Name
<input type="text" name="aField" ng-model="aField" required="" />

<br /><br />

<input type="button" ng-click="Save(formExample,formScopeObject)" />

</form>

Solution 6 - Angularjs

To expand on the answer by user1338062: A solution I have used multiple times to initialize something in my controller but had to wait until it was actually available to use:

var myVarWatch = $scope.$watch("myVar", function(){
    if(myVar){
        //do whatever init you need to
        myVarWatch();    //make sure you call this to remove the watch
    }
});

Solution 7 - Angularjs

For those using Angular 1.5, my solution was $watching the form on the $postlink stage:

$postLink() {
      this.$scope.$watch(() => this.$scope.form.$valid, () => {
      });
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsarunastView Question on Stackoverflow
Solution 1 - AngularjsJorgeView Answer on Stackoverflow
Solution 2 - AngularjsmichaelView Answer on Stackoverflow
Solution 3 - AngularjsLeOn - Han LiView Answer on Stackoverflow
Solution 4 - Angularjsuser1338062View Answer on Stackoverflow
Solution 5 - AngularjsPaulView Answer on Stackoverflow
Solution 6 - Angularjstkd_ajView Answer on Stackoverflow
Solution 7 - AngularjsPainy JamesView Answer on Stackoverflow