web-dev-qa-db-fra.com

Utiliser ui-router avec Bootstrap-ui modal

Je sais que cela a déjà été abordé à maintes reprises et que la plupart des articles font référence à ce code: Fenêtre modale avec une URL personnalisée dans AngularJS

Mais je ne comprends pas. Je ne trouve pas cela très clair du tout. J'ai aussi trouvé ceci jsfiddle qui était en fait génial, très utile sauf que cela n'ajoute pas l'URL et me permet d'utiliser le bouton de retour pour fermer le modal.


Edit: C’est ce pour quoi j’ai besoin d’aide.

Alors laissez-moi essayer d’expliquer ce que j’essaie d’atteindre. J'ai un formulaire pour ajouter un nouvel élément et j'ai un lien "ajouter un nouvel élément". Lorsque je clique sur "ajouter un nouvel élément", un modal apparaît avec le formulaire que j'ai créé "add-item.html". Il s’agit d’un nouvel état et l’url change en /add-item.Je peux remplir le formulaire, puis choisir d’enregistrer ou de fermer. Close, ferme le modal: p (c'est étrange). Mais je peux aussi cliquer en arrière pour fermer le modal et revenir à la page précédente (état).Je n'ai pas besoin d'aide avec Close pour le moment, car je ne parviens toujours pas à faire fonctionner le modal.


Ceci est mon code tel quel:

Contrôleur de navigation: (est-ce même le bon endroit pour mettre les fonctions modales?)}

angular.module('cbuiRouterApp')
  .controller('NavbarCtrl', function ($scope, $location, Auth, $modal) {
    $scope.menu = [{
      'title': 'Home',
      'link': '/'
    }];

    $scope.open = function(){

        // open modal whithout changing url
        $modal.open({
          templateUrl: 'components/new-item/new-item.html'
        });

        // I need to open popup via $state.go or something like this
        $scope.close = function(result){
          $modal.close(result);
        };
      };

    $scope.isCollapsed = true;
    $scope.isLoggedIn = Auth.isLoggedIn;
    $scope.isAdmin = Auth.isAdmin;
    $scope.getCurrentUser = Auth.getCurrentUser;

    $scope.logout = function() {
      Auth.logout();
      $location.path('/login');
    };

    $scope.isActive = function(route) {
      return route === $location.path();
    };
  });

Voici comment j'active le modal:

 <li ng-show='isLoggedIn()' ng-class='{active: isActive("/new-item")}'>
   <a href='javascript: void 0;' ng-click='open()'>New Item</a>
 </li>

new-item.html:

<div class="modal-header">
  <h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
  <ul>
    <li ng-repeat="item in items"><a ng-click="selected.item = item">{{ item }}</a></li>
  </ul>Selected:<b>{{ selected.item }}</b>
</div>
<div class="modal-footer">
  <button ng-click="ok()" class="btn btn-primary">OK</button>
  <button ng-click="close()" class="btn btn-primary">OK</button>
</div>

De plus, même si cela ouvre un modal, il ne le ferme pas car je ne pouvais pas le comprendre.

30
Daimz

Il est intuitif de penser à un modal en tant que composant d'affichage d'un état. Prenez une définition d'état avec un modèle de vue, un contrôleur et éventuellement quelques résolutions. Chacune de ces fonctionnalités s'applique également à la définition d'un modal. Allez un peu plus loin et liez l’entrée d’état à l’ouverture du modal et l’exit à la fermeture du modal. Si vous pouvez encapsuler toute la tuyauterie, vous disposez d’un mécanisme qui peut être utilisé comme un état avec ui-sref ou $state.go pour la bouton Précédent ou plusieurs déclencheurs spécifiques à un modal pour la sortie.

J'ai étudié cette assez abondamment , et mon approche consistait à créer un fournisseur d'état modal pouvant être utilisé de manière analogue à $stateProvider lors de la configuration d'un module pour définir des états liés à des modaux. À l'époque, j'étais particulièrement intéressé par l'unification du contrôle sur le renvoi modal par le biais d'événements d'état et modaux, ce qui devient plus compliqué que ce que vous demandez. Voici donc un exemple simplifié .

La clé consiste à faire du modal la responsabilité de l'état et à utiliser les points d'ancrage fournis par ce dernier pour maintenir l'état synchronisé avec les interactions indépendantes prises en charge par le modal via son étendue ou son interface utilisateur.

.provider('modalState', function($stateProvider) {
    var provider = this;
    this.$get = function() {
        return provider;
    }
    this.state = function(stateName, options) {
        var modalInstance;
        $stateProvider.state(stateName, {
            url: options.url,
            onEnter: function($modal, $state) {
                modalInstance = $modal.open(options);
                modalInstance.result['finally'](function() {
                    modalInstance = null;
                    if ($state.$current.name === stateName) {
                        $state.go('^');
                    }
                });
            },
            onExit: function() {
                if (modalInstance) {
                    modalInstance.close();
                }
            }
        });
    };
})

L'entrée d'état lance le modal. La sortie d'état le ferme. Le modal peut se fermer tout seul (ex: via un clic de fond), vous devez donc l'observer et mettre à jour l'état.

L'avantage de cette approche est que votre application continue à interagir principalement avec des états et des concepts liés aux états. Si vous décidez par la suite de transformer le modal en vue conventionnelle ou inversement, très peu de code doit être modifié.

51
Nathan Williams

Voici une provider qui améliore la solution @ nathan-williams en passant la section resolve à la controller:

.provider('modalState', ['$stateProvider', function($stateProvider) {
  var provider = this;

  this.$get = function() {
    return provider;
  }

  this.state = function(stateName, options) {
    var modalInstance;

    options.onEnter = onEnter;
    options.onExit = onExit;
    if (!options.resolve) options.resolve = [];

    var resolveKeys = angular.isArray(options.resolve) ? options.resolve : Object.keys(options.resolve);
    $stateProvider.state(stateName, omit(options, ['template', 'templateUrl', 'controller', 'controllerAs']));

    onEnter.$inject = ['$uibModal', '$state', '$timeout'].concat(resolveKeys);
    function onEnter($modal, $state, $timeout) {
      options.resolve = {};

      for (var i = onEnter.$inject.length - resolveKeys.length; i < onEnter.$inject.length; i++) {
        (function(key, val) {
          options.resolve[key] = function() { return val }
        })(onEnter.$inject[i], arguments[i]);
      }

      $timeout(function() { // to let populate $stateParams
        modalInstance = $modal.open(options);
        modalInstance.result.finally(function() {
          $timeout(function() { // to let populate $state.$current
            if ($state.$current.name === stateName)
              $state.go(options.parent || '^');
          });
        });
      });
    }

    function onExit() {
      if (modalInstance)
        modalInstance.close();
    }

    return provider;
  }
}]);

function omit(object, forbidenKeys) {
  var prunedObject = {};
  for (var key in object)
    if (forbidenKeys.indexOf(key) === -1)
      prunedObject[key] = object[key];
  return prunedObject;
}

puis utilisez-le comme ça:

.config(['modalStateProvider', function(modalStateProvider) {
  modalStateProvider
    .state('...', {
      url: '...',
      templateUrl: '...',
      controller: '...',
      resolve: {
        ...
      }
    })
}]);
7
Dawid Grzesiak

J'ai répondu à une question similaire et fourni un exemple ici:

Fenêtre modale avec URL personnalisée dans AngularJS

A un HTML complet et fonctionnel et un lien vers Plunker.

2
cornernote

Le modal lui-même n'a pas de fonction close (), je veux dire Si vous console.log ($ modal), vous pouvez voir qu'il n'y a qu'une fonction open ().

La fermeture du modal repose sur l'objet $ modalInstance, que vous pouvez utiliser dans votre modalController.

Donc, ceci: $ modal.close (result) n’est pas réellement une fonction!

Remarquer : console.log ($ modal); == >> résultat:

          Object { open: a.$get</k.open() }
           // see ? just open ! , no close !

Il existe un moyen de résoudre ce problème, notamment: 

Tout d’abord, vous devez définir un contrôleur dans votre modal comme ceci: 

   $modal.open({
      templateUrl: 'components/new-item/new-item.html',
      controller:"MyModalController"
    });

Et puis, plus tard: 

    app.controller('MyModalController',function($scope,$modalInstance){
      $scope.closeMyModal = function(){
       $modalInstance.close(result);
        }
       // Notice that, This $scope is a seperate scope from your NavbarCtrl,
       // If you want to have that scope here you must resolve it

   });
0
Milad