:::: MENU ::::

Des onglets avec AngularJs et ui-router

Le problème : créer des onglets, si possible de manière dynamique ( ng-repeat ).

Le problème bonus : avoir un url définit pour chaque onglet et pouvoir y accéder directement.

 

Voici une solution sous AngularJs avec angular-ui pour les onglets et ui-router pour générer les vues et les url

Création des onglets

On peut facilement mettre en place un petit système d’onglets avec les directives d’angular-ui :


<tabset>
  <tab heading="tab1">Contenu page 1</tab>
  <tab heading="tab2">Contenu page 2</tab>
</tabset>

Et voila pour les onglets ! Maintenant, de manière dynamique

<tabset>
  <tab ng-repeat="item in tabs" heading="{{item.titre}}">...</tab>
<tabset>
app.controller("myCtrl", function($scope, $state) {

  $scope.tabs = [
    { titre: "tab1"},
    { titre: "tab2"}
  ];

});

J’ai ici volontairement supprimé le contenu des pages. On va le charger à l’aide de notre routeur ( ce qui permettra d’en faire des templates par la même occasion )

 

Connexion des routes

Pour configurer les routes de nos onglets, on va utiliser le $stateProvider d’ui-router

var app = angular.module("myApp", ["ui.router", "ui.bootstrap"]);

app.config(function($stateProvider, $urlRouterProvider, $locationProvider){

   $stateProvider.state("configuration", {url:"/configuration", templateUrl:"conf.html" })
               .state("configuration.tab1", { url: "/tab1", templateUrl: "tab1.html" })
               .state("configuration.tab2", { url: "/tab2", templateUrl: "tab2.html" })
               .state("configuration.tab3", { url: "/tab3", templateUrl: "tab3.html" });
  });

  $urlRouterProvider.otherwise("/");
  $locationProvider.html5Mode(true);
});

On a ici une configuration de la route principale /configuration et de sous routes correspondantes à nos onglets. Chaque route possède une url et un template mais on pourrait aussi leur associer un contrôleur par exemple.

Pour intégrer nos vues aux onglets, il faut utiliser la directive ui-view d’ui router

<div ng-controller="myCtrl">

 <tabset>
 <tab
 ng-repeat="item in tabs" heading="{{item.titre}}" active="item.active" ui-sref="configuration.{{item.titre}}"
 </tab>
 </tabset>

 <div ui-view></div>

</div>

On ajoute aussi ui-sref qui permettra de placer automatiquement le lien correspondant. L’attribut active permet de sélectionner un onglet.

A partir de là, notre système fonctionne presque, la dernière étape est de mettre à jour l’onglet actif en fonction de l’url.

On reprend notre contrôleur :

app.controller("myCtrl", function($scope, $state) { 

 $scope.tabs = [
 { titre: "tab1"},
 { titre: "tab2"},
 { titre: "tab3"},
 ];

 $scope.tabs.forEach(function(item){
   item.active = $state.includes("configuration."+item.titre);
 });

 });

8 Comments

  • Répondre Olivier Thomas |

    Bonjour,

    Joli code, j’étais sur une approche similaire mais j’avais un petit dysfonctionnement ;

    Je pensais trouver une réponse ce un petit soucis mais je constate le même problème lorsque j’essaie d’utiliser ce code à la place…

    Quand j’entre l’url configuration/tab1 ou configuration/tab2 en venant d’un état autre que configuration, tout fonctionne, mais lorsque j’entre l’url configuration/tab2 à partir de l’url configuration/tab1, alors la vue change mais pas l’onglet.

    Autrement dit la valeur de item.active semble être calculée lors du chargement de la page, (grâce à ton contrôleur), et lors du click sur l’onglet (grace à la directive de angular-ui-tab) mais pas lors du changement d’état (ui-router n’appelle pas le contrôleur .)

    Est-ce que c’est lié à mon appli ?

    J’aurais vraiment voulu comprendre comment faire pour que lors du changement d’état, le contrôleur soit bien exécuté ce qui ne semble pas être le cas.

    • Répondre maxdow |

      Bonjour,

      je ne pense pas que cela soit lié à ton appli. Tu peux peut être essayer de rajouter un bind sur l’évenement $locationChangeSuccess dans ton contrôleur qui g^re les onglets ?

      $scope.$on(« $locationChangeSuccess », function() {

      // item.active = $state.current.name , par exemple à adapter en suivant

      });

      • Répondre Olivier Thomas |

        Ta piste semble bonne… En fait elle aide à comprendre pourquoi ça ne fonctionne pas…

        Lorsque j’ai utilisé :

        $scope.$on(« $locationChangeSuccess », function() {
        console.log($state.current.name);
        });

        On s’aperçoit que

        – current,name est évalué et change lors d’un click sur les onglets.
        – current,name est évalué mais reste sur le nom de l’état précédant lorsque qu’on change d’url à partir d’un autre lien.

        C’est étrange. Je ne vois pas d’où vient la différence. Je continu à chercher.

        En attendant, il faut veiller à ne pas poser de liens qui passe d’un état à un autre car il rend incohérent le couple onglet/contenu.

        • Répondre maxdow |

          Peut être que la différence vient du fait que $locationChange est un event propre à angular. La gestion des états de routes passant par ui-router , tu aura peut être plus de chance avec $stateChangeSuccess qui est un event de ui-router ?

          Cela ne devrait pas être un problème de changer d’états , sinon c’est qu’il y a un soucis d’incohérence quelque part et ça finira par casser ou nécessiter une verrue. Il faudrait que je reprenne cet article pour rajouter une note sur cet aspect ;)

          • Olivier Thomas |

            Bingo… Tout fonctionne, le code suivant fonctionne

            app.controller(« myCtrl », function($scope, $state) {

            $scope.tabs = [
            { titre: « tab1 »},
            { titre: « tab2 »},
            { titre: « tab3 »},
            ];

            $scope.$on(« $stateChangeSuccess », function(){
            $scope.tabs.forEach(function(item){
            item.active = $state.includes(« configuration. »+item.titre);
            });
            });
            });

            Merci pour tout !

  • Répondre Alexandre D. |

    Je confirme qu’il faut bien ajouter le $on(« $locationChangeSuccess »…
    Sans cela, l’application est incapable de déterminer dans quel état elle est.
    Merci pour ce code maxdow au passage.

So, what do you think ?