web-dev-qa-db-fra.com

AngularJs ne peut pas accéder à un objet formulaire dans le contrôleur ($ scope)

J'utilise plus spécifiquement bootstrap-ui sous Windows. Et j'ai un formulaire dans un modal, ce que je veux, c'est instancier un objet de validation de formulaire. Donc, fondamentalement, je fais ceci:

<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

Je peux voir l’objet formulaire dans le fichier html sans problème, toutefois si je souhaite accéder à l’objet validation du formulaire à partir du contrôleur. Il me sort juste un objet vide. Voici un exemple de contrôleur:

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

Quelles pourraient être les raisons pour lesquelles je ne peux pas accéder à $scope.form à partir du contrôleur?

65
sarunast

Pour ceux qui n'utilisent pas $scope, mais plutôt this, dans leur contrôleur, vous devrez ajouter l'alias du contrôleur précédant le nom du formulaire. Par exemple:

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

puis sur le contrôleur:

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

J'espère qu'il contribuera également à l'ensemble des réponses.

72
Jorge

La manière normale si ng-controller est un parent de l'élément de formulaire: , Supprimez cette ligne:

$scope.form = {};

Si angular définit le formulaire sur vos contrôleurs $scope, vous écrasez-le avec un objet vide.


Comme l'OP l'a indiqué, ce n'est pas le cas ici. Il utilise $modal.open, de sorte que le contrôleur n'est pas le parent du formulaire. Je ne connais pas de solution intéressante. Mais ce problème peut être piraté:

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

et dans votre contrôleur:

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

et plus tard dans votre fonction de mise à jour:

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

};
45
michael

Regardez le code source du 'modal' de bootstrap d'interface utilisateur angulaire, vous verrez que la directive a

transclude: true

Cela signifie que la fenêtre modale créera une nouvelle portée enfant dont le parent ici est la valeur du contrôleur $, en tant que parent de la portée de la directive. Ensuite, le 'formulaire' ne peut être accessible que par la portée enfant nouvellement créée.

Une solution consiste à définir une variable dans l’étendue du contrôleur, comme 

$scope.forms = {};

Ensuite, pour le nom de form, nous utilisons quelque chose comme forms.formName1. De cette façon, nous pourrions toujours y accéder à partir de notre contrôleur en appelant simplement $scope.forms.formName1.

Cela fonctionne car le mécanisme d'héritage dans JS est une chaîne de prototypes. Lorsque la portée enfant tente de créer le forms.formName1, elle tente d'abord de trouver l'objet de formulaire dans sa propre portée, qui ne l'a définitivement pas, car il est créé à la volée. Ensuite, il essaiera de le trouver depuis le parent (jusqu’à la chaîne de prototypes) et, comme nous l’avons défini dans la portée du contrôleur, il utilise cet objet 'forms' que nous avons créé pour définir la variable formName1. En conséquence, nous pourrions toujours l’utiliser dans notre contrôleur pour effectuer nos tâches telles que: 

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

Pour en savoir plus sur la transclusion, regardez ci-dessous la vidéo de Misco de 45 minutes. (C'est probablement l'explication la plus précise de ce que j'ai jamais trouvé des portées transcludes !!!)

www.youtube.com/watch?v=WqmeI5fZcho

21
Leon li

J'utilise l'approche documentée.

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

ainsi, utilisateur, le nom du formulaire, cliquez sur "save", par exemple, passez simplement le formName en tant que paramètre et le formulaire sera disponible dans la méthode save (où formScopeObject est créé en fonction des spécifications de ng-models que vous avez définies dans votre formulaire OR si vous modifiez, il s’agit de l’objet stockant l’élément en cours de modification, c’est-à-dire un compte utilisateur)

<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>
6
Paul

Pas besoin de la ruse ng-init, car le problème est que $scope.form n'est pas défini lors de l'exécution du code du contrôleur. Supprimez l'initialisation form = {} et accédez au formulaire à l'aide d'une montre:

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

Pour ceux qui utilisaient Angular 1.5, ma solution était de regarder le formulaire à l’étape $ postlink:

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

Pour développer la réponse de l'utilisateur1338062: Une solution que j'ai utilisée plusieurs fois pour initialiser quelque chose dans mon contrôleur mais que j'ai dû attendre jusqu'à ce qu'elle soit réellement disponible pour être utilisée:

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