66

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 ?

7 Answers 7

76

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for sharing even if the question had an accepted answer, this was just what I needed to know.
This helped a lot -- thank you! In my case, it was a directive's controller using bindToController and controllerAs. E.g., in the directive: ... controller: '<Controller Name>', controllerAs: 'vm', bindToController: true In the template: <form name="vm.<form name>">... In the controller: // access form with vm.<form name>
Excellent answer!! Exactly what my issue was.Just as a side note if anyone comes across this and wonders about the use of 'this', it is commonly used in the 'controller As' syntax that is starting to be used more and more.
46

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); 

};

9 Comments

@Tamy how did you call update? $scope.form is not available during controller instantiation.
Simply <button class="btn btn-primary" ng-click="update()">Update</button>
@Michael Well I discovered if I change the form name to name='form.something' then I get values ... Any Ideas what's happening ? (I will try your suggestion)
@Michael I can confirm that your way works. I just need to decide to use the name='form.something' or your way. Thanks.
Thanks for this! I was pulling my hair out until I realised you need to use form.something. It is the same issue when using ng-model (you need to capture in an object, rather than a simple var). I thought I understood scopes, but it turns out I don't yet... :)
|
21

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

Comments

7

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>

Comments

6

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) {
  ...
});

Comments

0

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
    }
});

Comments

0

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

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

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.