web-dev-qa-db-fra.com

Objet de formulaire de boîte de dialogue modulaire AngularJS non défini dans le contrôleur

Nous avons une page qui ouvre une boîte de dialogue modale avec un formulaire comme ci-dessous. Cependant, lorsque nous frappons le contrôleur qui doit gérer l'action du formulaire, l'objet de formulaire n'est pas défini et je suis trop un débutant Angular pour comprendre pourquoi ...

C'est le contrôleur de page parent qui détient la fonction pour ouvrir la boîte de dialogue modale:

app.controller('organisationStructureController', ['$scope', ..., '$modal', function ($scope, ..., $modal) {

    $scope.openInvitationDialog = function (targetOrganisationId) {
      $modal.open({
          templateUrl: 'send-invitation.html',
          controller: 'sendInvitationController',
          resolve: {$targetOrganisationId: function () {
            return targetOrganisationId;
          }
          }
        }
      );
    };

sur une page comme celle-ci:

// inside a loop over organisations
<a ng-click="openInvitationDialog({{organisation.id}})">Invite new member</a>

le html de dialogue d'invitation ressemble à ceci:

    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <!-- ... -->
            </div>
            <div class="modal-body">
                <form name="invitationForm">

                    <div class="form-group">
                        <label for="email" style="color:white;">Email</label>
                        <input type="email" class="form-control"  autocomplete="off" placeholder="New member email" id="email" name="email" ng-model="invitation.email" required="true"/>
                        <span class="error animated fadeIn" ng-show="invitationForm.email.$dirty && invitationForm.email.$error.required">Please enter an email address!</span>
                        <span class="error animated fadeIn" ng-show="invitationForm.email.$error.email">Invalid email</span>
                    </div>

                    <!-- ... -->

                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
                        <button type="submit" class="btn btn-primary" ng-click="sendInvitation()">Invite</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

Le contrôleur qui devrait gérer l'invitation est ailleurs:

  app.controller('sendInvitationController', ['$targetOrganisationId', '$scope', ...,
    function ($targetOrganisationId, $scope, ...) {

    $scope.invitation = {
      // ...
      targetOrganisation: {
        id: $targetOrganisationId
      }
    };

    $scope.sendInvitation = function () {

      // $scope.invitationForm is undefined
      if ($scope.invitationForm.$invalid) {
        return false;
      }

      // send the invitation...

    };
  }]);

Alors, quelle est la bonne façon d'obtenir la portée du formulaire dans le contrôleur?

Peut-être que je dois injecter $modal Dans le sendInvitationController et y ajouter la fonction sendInvitation? Mais quand je le fais, l'action n'entre jamais dans le contrôleur. Ou dois-je ajouter la fonction qui gère l'action d'envoi à $modal.open({ ... Au lieu de référencer le contrôleur? Bien que je préfère de loin avoir le sendInvitationController dans son propre fichier et portée.

Merci pour toute aide!

MODIFIER

Nous avons trouvé plusieurs choses qui nous ont aidés à créer une solution de contournement et qui pourraient aider quelqu'un à répondre à la question elle-même:

  1. l'objet $scope.invitation n'est pas indéfini dans le sendInvitationController mais contient les données correctes, tandis que $scope.invitationForm reste indéfini.
  2. à l'intérieur du fichier send-invitation.html, nous pouvons accéder à $scope.invitationForm.$invalid et faire la validation directement: <button type="button" ng-click="sendInvitation()" ng-disabled="invitationForm.$invalid">Invite</button>

La question est donc: pourquoi la liaison de l'objet invitationForm à $scope Échoue lors de la soumission alors que le modèle de formulaire se lie correctement?

52
Pete

J'ai eu le même problème et j'ai pu le résoudre en définissant l'objet de formulaire dans la portée du contrôleur modal. Pour faire fonctionner votre code, mettez, par exemple, $scope.form = {}; au début de votre contrôleur et remplacez votre balise de formulaire par <form name="form.invitation">. Ensuite $scope.form.invitation.$invalid doit être rempli.

78
Robin

Mise à jour novembre 2014: à partir de angular-ui-bootstrap 0.12.0 la portée de transclusion est fusionnée avec la portée du contrôleur. Il n'y a rien à faire.

Avant 0.12.:

Pour mettre invitationForm directement dans la portée de votre contrôleur parent, vous devez contourner la portée transclue de cette façon:

<form name="$parent.invitationForm">

Ci-dessus créera automatiquement un objet de formulaire dans votre contrôleur parent. Pas besoin de pré-initialisation, de longs chemins d'objet ou de passage par événement. Accédez-y simplement avec $scope.invitationForm une fois le modal ouvert.

39
gertas

La réponse à la question "Pourquoi?" est "portée". tl; dr vous avez créé une nouvelle portée avec la boîte de dialogue modale qui a caché l'objet formulaire de la portée de votre contrôleur.

Si nous simplifions votre code, nous obtenons grosso modo ce qui suit:

<div ng-ctrl="organizeCtrl">
  <modal-dialog>
    <form name="invitationForm">
      <input type="email" ng-model="invitation.email" placeholder="Enter email..." />
      <input type="submit" ng-click="sendInvitation()" text="Invite!" />
      <input type="button" ng-click="cancel()" text="Cancel  :(" />
    </form>
  </modal-dialog>
</div>

(Il s'agit d'une version très simplifiée qui devrait avoir toujours tous les composants de base.) Maintenant, regardons où les étendues sont créées et ce qui est injecté dans leur.

<div ng-ctrl="sendInvitationController">
<!-- scope created above with "invitation" and "sendInvitation" from sendInvitationController -->
  <modal-dialog>
  <!-- scope created above for the modal dialog transclude -->
    <form name="invitationForm">
    <!-- add "invitationForm" to the modal dialog's scope -->
      <input type="email" ng-model="invitation.email" placeholder="Enter email..." />
      <input type="submit" ng-click="sendInvitation()" text="Invite!" />
      <input type="button" ng-click="cancel()" text="Cancel  :(" />
    </form>
  </modal-dialog>
</div>

Ici, vous pouvez voir qu'une nouvelle portée enfant a été créée dans le <modal-dialog> élément et que est l'endroit où l'objet invitationForm est réellement ajouté. C'est pourquoi vous ne pouvez pas voir l'objet dans le sendInvitationController mais vous pouvez le voir sur les boutons pour ng-disabled. Si vous souhaitez pouvoir accéder à la construction de formulaire en dehors de <modal-dialog> élément (par exemple dans le sendInvitationController), vous devrez le passer dans l'appel de fonction:

<div ng-ctrl="organizeCtrl">
  <modal-dialog>
    <form name="invitationForm">
      <input type="email" ng-model="invitation.email" placeholder="Enter email..." />
      <input type="submit" ng-click="sendInvitation(invitationForm)" text="Invite!" />
      <input type="button" ng-click="cancel()" text="Cancel  :(" />
    </form>
  </modal-dialog>
</div>

Le contrôleur acceptant le formulaire d'invitation comme paramètre de la fonction sendInvitation:

app.controller('sendInvitationController', ['$targetOrganisationId', '$scope', ...,
  function ($targetOrganisationId, $scope, ...) {
  $scope.invitation = {
    targetOrganisation: {
      id: $targetOrganisationId
    }
  };
  $scope.sendInvitation = function (form) {
    if (form.$invalid) {
      return false;
    }
    // send the invitation...
  };
}]);

@Robin a identifié l'autre solution, spécifiquement pour créer un objet enraciné dans la portée de sendInvitationController, puis attacher le formulaire directement à cet objet, en s'appuyant sur le mécanisme de traversée de portée d'Angular pour trouver l'objet form sur le champ d'application en dehors du <modal-dialog> et attachez-y l'objet formulaire. Notez que si vous n'avez pas spécifié $scope.form = {} dans le sendInvitationController, Angular aurait créé un nouvel objet pour form sur la portée du <modal-dialog> et vous n'auriez toujours pas pu y accéder dans le sendInvitationController.

J'espère que cela vous aidera, vous ou d'autres personnes, à en savoir plus sur la portée Angular scoping.

19
squid314

J'ai fait travailler le mien comme ceci:

$modal.open({
  templateUrl: 'send-invitation.html',
  controller: 'sendInvitationController',
  scope: $scope // <-- I added this
}

Pas de nom de formulaire, pas de $parent. J'utilise AngularUI Bootstrap version 0.12.1.

J'ai été averti de cette solution par this .

2
Jason Swett
$mdDialog.show({
                locals: {alert:"display meassage"},
                controller: DialogController,
                templateUrl: 'views/dialog.html',
                parent: angular.element(document.body),
                clickOutsideToClose:true,
                backdrop: true,
                keyboard: true,
                backdropClick: true,

            })
0
Jijo Paulose