80 votes

Problèmes de portée avec la modale d'Angular UI

J'ai du mal à comprendre/utiliser les scopes pour une modale angular UI.

Bien que ce ne soit pas immédiatement apparent ici, j'ai les modules et tout est correctement configuré (pour autant que je puisse dire), mais ces échantillons de code en particulier sont ceux où je trouve le bug.

index.html (la partie importante)

<div class="btn-group">
    <button class="btn dropdown-toggle btn-mini" data-toggle="dropdown">
        Actions
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu pull-right text-left">
        <li><a ng-click="addSimpleGroup()">Add Simple</a></li>
        <li><a ng-click="open()">Add Custom</a></li>
        <li class="divider"></li>
        <li><a ng-click="doBulkDelete()">Remove Selected</a></li>
    </ul>
</div>

Controller.js (encore une fois, la partie importante)

MyApp.controller('AppListCtrl', function($scope, $modal){
    $scope.name = 'New Name';
    $scope.groupType = 'New Type';

    $scope.open = function(){
        var modalInstance = $modal.open({
            templateUrl: 'partials/create.html',
            controller: 'AppCreateCtrl'
        });
        modalInstance.result.then(function(response){

            // outputs an object {name: 'Custom Name', groupType: 'Custom Type'}
            // despite the user entering customized values
            console.log('response', response);

            // outputs "New Name", which is fine, makes sense to me.                
            console.log('name', $scope.name);

        });
    };
});

MyApp.controller('AppCreateCtrl', function($scope, $modalInstance){
    $scope.name = 'Custom Name';
    $scope.groupType = 'Custom Type';

    $scope.ok = function(){

        // outputs 'Custom Name' despite user entering "TEST 1"
        console.log('create name', $scope.name);

        // outputs 'Custom Type' despite user entering "TEST 2"
        console.log('create type', $scope.groupType);

        // outputs the $scope for AppCreateCtrl but name and groupType
        // still show as "Custom Name" and "Custom Type"
        // $scope.$id is "007"
        console.log('scope', $scope);

        // outputs what looks like the scope, but in this object the
        // values for name and groupType are "TEST 1" and "TEST 2" as expected.
        // this.$id is set to "009" so this != $scope
        console.log('this', this);

        // based on what modalInstance.result.then() is saying,
        // the values that are in this object are the original $scope ones
        // not the ones the user has just entered in the UI. no data binding?
        $modalInstance.close({
            name: $scope.name,
            groupType: $scope.groupType
        });
    };
});

create.html (dans son intégralité)

<div class="modal-header">
    <button type="button" class="close" ng-click="cancel()">x</button>
    <h3 id="myModalLabel">Add Template Group</h3>
</div>
<div class="modal-body">
    <form>
        <fieldset>
            <label for="name">Group Name:</label>
            <input type="text" name="name" ng-model="name" />           
            <label for="groupType">Group Type:</label>
            <input type="text" name="groupType" ng-model="groupType" />
        </fieldset>
    </form>
</div>
<div class="modal-footer">
    <button class="btn" ng-click="cancel()">Cancel</button>
    <button class="btn btn-primary" ng-click="ok()">Add</button>
</div>

Ma question est donc la suivante : pourquoi la portée n'est-elle pas liée à l'interface utilisateur ? this ont les valeurs personnalisées, mais $scope ne le fait pas ?

J'ai essayé d'ajouter ng-controller="AppCreateCtrl" au div du corps dans create.html, mais cela a donné lieu à une erreur : "Unknown provider : $modalInstanceProvider <- $modalInstance" donc pas de chance là.

A ce stade, ma seule option est de renvoyer un objet avec this.name y this.groupType au lieu d'utiliser $scope mais ça ne va pas.

0 votes

Bonne discussion sur la portée modale ici : github.com/mgcrea/angular-strap/issues/14

66voto

Nikos Paraskevopoulos Points 14656

Lorsque des scopes imbriqués sont impliqués, ne liez pas <input> directement aux membres de l'étendue :

<input ng-model="name" /> <!-- NO -->

Liez-les à au moins un niveau plus profond :

<input ng-model="form.name" /> <!-- YES -->

La raison en est que les scopes héritent prototypiquement de leur scope parent. Ainsi, lorsque vous définissez des membres de premier niveau, ceux-ci sont définis directement sur l'étendue enfant, sans affecter le parent. En revanche, lors de la liaison à des champs imbriqués ( form.name ) le membre form est lu à partir de l'étendue parent, de sorte que l'accès à l'élément name accède à la bonne cible.

Lire une description plus détaillée aquí .

14 votes

Si le remplacement par 'form.name' n'a rien donné, le remplacement par ng-model="$parent.name" a réglé le problème. Merci ! (et merci aussi pour le matériel de lecture. Je n'avais pas encore vu celui-ci).

1 votes

Si vous utilisez le controller as vous éviterez ainsi les problèmes de portée imbriquée comme celui-ci

0 votes

Quel est le controller as la syntaxe ?

59voto

Jason Swett Points 8368

J'ai réussi à faire fonctionner le mien comme ça :

var modalInstance = $modal.open({
  templateUrl: 'partials/create.html',
  controller: 'AppCreateCtrl',
  scope: $scope // <-- I added this
});

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

Cette solution m'a été suggérée par este .

7voto

gertas Points 7113

Mise à jour Nov 2014 :

En fait, votre code devrait fonctionner après la mise à jour vers ui-bootstrap 0.12.0. La portée du transclus est fusionnée avec celle du contrôleur, donc plus besoin de $parent o form. truc.

Avant 0.12.0 :

La modale utilise la transclusion pour insérer son contenu. Grâce à ngForm vous pouvez contrôler la portée en name attribut. Ainsi, pour échapper à la portée transcluse, il suffit de modifier le formulaire de cette façon :

<form name="$parent">

o

<form name="$parent.myFormData">

Les données du modèle seront disponibles dans la portée du contrôleur.

0 votes

Pour être clair, dites-vous que la portée du contrôleur à l'intérieur duquel vous appelez $modal devrait être disponible pour le contrôleur affecté à la modale ?

0 votes

Non, le problème était que même le contrôleur d'instance modal n'était pas facilement accessible pour les formulaires. Pour accéder au contrôleur qui ouvre la modale il suffit de mettre scope:$scope dans $modal.open params map.

0 votes

Voulez-vous dire Angular UI ? Est-ce différent de UI Bootstrap ?

1voto

ali Shoaib Points 11
$scope.open = function () {

          var modalInstance = $uibModal.open({
              animation: $scope.animationsEnabled,
              templateUrl: 'myModalContent.html',
              controller: 'salespersonReportController',
              //size: size
              scope: $scope
            });

      };

cela fonctionne pour moi scope : $scope merci à Jason Swett

1voto

JBRandri Points 247

J'ai ajouté scope:$scope puis ça marche .Cool

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X