javascript with Definir estilo de tabulação ativa com o AngularJS




set document title javascript (15)

Uma maneira de resolver isso sem depender de URLs é adicionar um atributo personalizado a cada parcial durante a configuração de $routeProvider , desta forma:

$routeProvider.
    when('/dashboard', {
        templateUrl: 'partials/dashboard.html',
        controller: widgetsController,
        activetab: 'dashboard'
    }).
    when('/lab', {
        templateUrl: 'partials/lab.html',
        controller: widgetsController,
        activetab: 'lab'
    });

Exponha $route no seu controlador:

function widgetsController($scope, $route) {
    $scope.$route = $route;
}

Defina a classe active base na guia ativa atual:

<li ng-class="{active: $route.current.activetab == 'dashboard'}"></li>
<li ng-class="{active: $route.current.activetab == 'lab'}"></li>

Eu tenho rotas definidas no AngularJS assim:

$routeProvider
    .when('/dashboard', {templateUrl:'partials/dashboard', controller:widgetsController})
    .when('/lab', {templateUrl:'partials/lab', controller:widgetsController})

Eu tenho alguns links no topbar com o nome de guias. Como posso adicionar uma classe 'ativa' a uma guia, dependendo do modelo ou URL atual?


Solução mais simples aqui:

Como definir a classe ativa bootstrap navbar com o Angular JS?

Qual é:

Use o ng-controller para executar um único controlador fora do ng-view:

<div class="collapse navbar-collapse" ng-controller="HeaderController">
    <ul class="nav navbar-nav">
        <li ng-class="{ active: isActive('/')}"><a href="/">Home</a></li>
        <li ng-class="{ active: isActive('/dogs')}"><a href="/dogs">Dogs</a></li>
        <li ng-class="{ active: isActive('/cats')}"><a href="/cats">Cats</a></li>
    </ul>
</div>
<div ng-view></div>

e inclua em controllers.js:

function HeaderController($scope, $location) 
{ 
    $scope.isActive = function (viewLocation) { 
        return viewLocation === $location.path();
    };
}

@ rob-juurlink Eu melhorei um pouco a sua solução:

em vez de cada rota que precisa de uma guia ativa; e precisando definir a guia ativa em cada controlador eu faço isso:

var App = angular.module('App',[]);
App.config(['$routeProvider', function($routeProvider){
  $routeProvider.
  when('/dashboard', {
    templateUrl: 'partials/dashboard.html',
    controller: Ctrl1
  }).
  when('/lab', {
    templateUrl: 'partials/lab.html',
    controller: Ctrl2
  });
}]).run(['$rootScope', '$location', function($rootScope, $location){
   var path = function() { return $location.path();};
   $rootScope.$watch(path, function(newVal, oldVal){
     $rootScope.activetab = newVal;
   });
}]);

E o HTML se parece com isso. O activetab é apenas o URL relacionado a esse caminho. Isso apenas elimina a necessidade de adicionar código em cada controlador (arrastando dependências como $ route e $ rootScope se esta for a única razão pela qual eles são usados)

<ul>
    <li ng-class="{active: activetab=='/dashboard'}">
       <a href="#/dashboard">dashboard</a>
    </li>
    <li ng-class="{active: activetab=='/lab'}">
       <a href="#/lab">lab</a>
    </li>
</ul>

Eu achei a resposta da XMLilley a melhor e mais adaptável e não intrusiva.

No entanto, tive uma pequena falha.

Para uso com o bootstrap nav, aqui está como eu o modifiquei:

app.directive('activeTab', function ($location) {
    return {
      link: function postLink(scope, element, attrs) {
        scope.$on("$routeChangeSuccess", function (event, current, previous) {
            /*  designed for full re-usability at any path, any level, by using 
                data from attrs
                declare like this: <li class="nav_tab"><a href="#/home" 
                                   active-tab="1">HOME</a></li> 
            */
            if(attrs.href!=undefined){// this directive is called twice for some reason
                // this var grabs the tab-level off the attribute, or defaults to 1
                var pathLevel = attrs.activeTab || 1,
                // this var finds what the path is at the level specified
                    pathToCheck = $location.path().split('/')[pathLevel],
                // this var finds grabs the same level of the href attribute
                    tabLink = attrs.href.split('/')[pathLevel];
                // now compare the two:
                if (pathToCheck === tabLink) {
                  element.parent().addClass("active");//parent to get the <li>
                }
                else {
                  element.parent().removeClass("active");
                }
            }
        });
      }
    };
  });

Eu adicionei "if (attrs.href! = Undefined)" porque esta função é chamada com atenção duas vezes, a segunda vez produzindo um erro.

Quanto ao html:

<ul class="nav nav-tabs">
   <li class="active" active-tab="1"><a href="#/accueil" active-tab="1">Accueil</a></li>
   <li><a active-tab="1" href="#/news">News</a></li>
   <li><a active-tab="1" href="#/photos" >Photos</a></li>
   <li><a active-tab="1" href="#/contact">Contact</a></li>
</ul>

Uma maneira de fazer isso seria usando a diretiva ngClass e o serviço $ location. Em seu modelo, você poderia fazer:

ng-class="{active:isActive('/dashboard')}"

onde isActive seria uma função em um escopo definido como este:

myApp.controller('MyCtrl', function($scope, $location) {
    $scope.isActive = function(route) {
        return route === $location.path();
    }
});

Aqui está o jsFiddle completo: http://jsfiddle.net/pkozlowski_opensource/KzAfG/

Repetir ng-class="{active:isActive('/dashboard')}" em cada guia de navegação pode ser entediante (se você tiver muitas guias) para que essa lógica seja candidata a uma diretiva muito simples.


Seguindo o conselho de Pavel para usar uma diretiva personalizada, aqui está uma versão que requer adição de carga ao routeConfig, é super declarativa e pode ser adaptada para reagir a qualquer nível do caminho, simplesmente mudando qual slice() dele você está prestando atenção.

app.directive('detectActiveTab', function ($location) {
    return {
      link: function postLink(scope, element, attrs) {
        scope.$on("$routeChangeSuccess", function (event, current, previous) {
            /*  
                Designed for full re-usability at any path, any level, by using 
                data from attrs. Declare like this: 
                <li class="nav_tab">
                  <a href="#/home" detect-active-tab="1">HOME</a>
                </li> 
            */

            // This var grabs the tab-level off the attribute, or defaults to 1
            var pathLevel = attrs.detectActiveTab || 1,
            // This var finds what the path is at the level specified
                pathToCheck = $location.path().split('/')[pathLevel] || 
                  "current $location.path doesn't reach this level",
            // This var finds grabs the same level of the href attribute
                tabLink = attrs.href.split('/')[pathLevel] || 
                  "href doesn't include this level";
            // Above, we use the logical 'or' operator to provide a default value
            // in cases where 'undefined' would otherwise be returned.
            // This prevents cases where undefined===undefined, 
            // possibly causing multiple tabs to be 'active'.

            // now compare the two:
            if (pathToCheck === tabLink) {
              element.addClass("active");
            }
            else {
              element.removeClass("active");
            }
        });
      }
    };
  });

Estamos realizando nossos objetivos ouvindo o evento $routeChangeSuccess , em vez de colocar um $watch no caminho. Trabalho sob a crença de que isso significa que a lógica deve ser executada com menos frequência, pois acho que os relógios são acionados em cada ciclo de $digest .

Invoque-o passando seu argumento de nível de caminho na declaração de diretiva. Isso especifica em qual parte do $ location.path () atual você deseja corresponder seu atributo href .

<li class="nav_tab"><a href="#/home" detect-active-tab="1">HOME</a></li>

Portanto, se suas guias reagirem ao nível base do caminho, faça o argumento '1'. Assim, quando location.path () for "/ home", ele corresponderá ao "# / home" no href . Se você tiver guias que devem reagir ao segundo nível, ou terceiro, ou 11 do caminho, ajuste de acordo. Este corte de 1 ou maior irá ignorar o nefasto '#' no href, que viverá no índice 0.

O único requisito é que você invoque em um <a> , pois o elemento está assumindo a presença de um atributo href , que será comparado ao caminho atual. No entanto, você pode adaptar-se facilmente para ler / gravar um elemento pai ou filho, se preferir invocar o <li> ou algo assim. Eu cavo isso porque você pode reutilizá-lo em muitos contextos simplesmente variando o argumento pathLevel. Se a profundidade a ser lida fosse assumida na lógica, você precisaria de várias versões da diretiva para usar com várias partes da navegação.

EDITAR 3/18/14: A solução foi inadequadamente generalizada e seria ativada se você definisse um arg para o valor de 'activeTab' que retornou undefined relação a ambos os $location.path() e href do elemento. Porque: undefined === undefined . Atualizado para corrigir essa condição.

Enquanto trabalhava nisso, percebi que deveria ter sido uma versão que você pode declarar em um elemento pai, com uma estrutura de modelo como esta:

<nav id="header_tabs" find-active-tab="1">
    <a href="#/home" class="nav_tab">HOME</a>
    <a href="#/finance" class="nav_tab">Finance</a>
    <a href="#/hr" class="nav_tab">Human Resources</a>
    <a href="#/quarterly" class="nav_tab">Quarterly</a>
</nav>

Observe que esta versão não se parece mais remotamente com o HTML do estilo Bootstrap. Mas, é mais moderno e usa menos elementos, então sou parcial nisso. Esta versão da diretiva, mais o original, estão agora disponíveis no Github como um módulo drop-in que você pode declarar como uma dependência. Eu ficaria feliz em fazer um Bower, se alguém realmente usá-los.

Além disso, se você quiser uma versão compatível com bootstrap que inclua <li> , você pode ir com o módulo Abas angular-ui-bootstrap , que eu acho que saiu após este post original, e que talvez seja ainda mais declarativo do que isso 1. É menos conciso para coisas básicas, mas oferece algumas opções adicionais, como guias desativadas e eventos declarativos que são ativados e desativados.


Veio aqui para a solução .. embora acima soluções estão funcionando bem, mas encontrou-os pouco complexo desnecessário. Para pessoas que ainda buscam uma solução fácil e organizada, ela fará a tarefa com perfeição.

<section ng-init="tab=1">
                <ul class="nav nav-tabs">
                    <li ng-class="{active: tab == 1}"><a ng-click="tab=1" href="#showitem">View Inventory</a></li>
                    <li ng-class="{active: tab == 2}"><a ng-click="tab=2" href="#additem">Add new item</a></li>
                    <li ng-class="{active: tab == 3}"><a ng-click="tab=3" href="#solditem">Sold item</a></li>
                </ul>
            </section>

uma maneira alternativa é usar ui-sref-active

Uma diretiva trabalhando em conjunto com ui-sref para adicionar classes a um elemento quando o estado da diretiva ui-sref relacionada estiver ativo e removê-las quando estiver inativo. O principal caso de uso é simplificar a aparência especial dos menus de navegação com base no ui-sref, fazendo com que o botão de menu do estado "ativo" apareça diferente, diferenciando-o dos itens de menu inativos.

Uso:

ui-sref-active = 'class1 class2 class3' - as classes "class1", "class2" e "class3" são adicionadas ao elemento diretivo quando o estado do ui-sref relacionado está ativo e removido quando está inativo.

Exemplo:
Dado o seguinte modelo,

<ul>
  <li ui-sref-active="active" class="item">
    <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
  </li>
  <!-- ... -->
</ul>

quando o estado do aplicativo for "app.user" e contiver o parâmetro de estado "user" com o valor "bilbobaggins", o HTML resultante aparecerá como

<ul>
  <li ui-sref-active="active" class="item active">
    <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
  </li>
  <!-- ... -->
</ul>

O nome da classe é interpolado uma vez durante o tempo do link de diretivas (quaisquer outras alterações no valor interpolado são ignoradas). Várias classes podem ser especificadas em um formato separado por espaço.

Use a diretiva ui-sref-opts para passar as opções para $ state.go (). Exemplo:

<a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>

'use strict';

angular.module('cloudApp')
  .controller('MenuController', function ($scope, $location, CloudAuth) {
    $scope.menu = [
      {
        'title': 'Dashboard',
        'iconClass': 'fa fa-dashboard',
        'link': '/dashboard',
        'active': true
      },
      {
        'title': 'Devices',
        'iconClass': 'fa fa-star',
        'link': '/devices'
      },
      {
        'title': 'Settings',
        'iconClass': 'fa fa-gears',
        'link': '/settings'
      }
    ];
    $location.path('/dashboard');
    $scope.isLoggedIn = CloudAuth.isLoggedIn;
    $scope.isAdmin = CloudAuth.isAdmin;
    $scope.isActive = function(route) {
      return route === $location.path();
    };
  });

E use o abaixo no modelo:

<li role="presentation" ng-class="{active:isActive(menuItem.link)}" ng-repeat="menuItem in menu"><a href="{{menuItem.link}}"><i class="{{menuItem.iconClass}}"></i>&nbsp;&nbsp;{{menuItem.title}}</a></li>

Eu precisava de uma solução que não exigisse mudanças nos controladores, porque para algumas páginas nós apenas renderizamos modelos e não há nenhum controlador. Graças aos comentaristas anteriores que sugeriram usar o $routeChangeSuccess eu $routeChangeSuccess algo parecido com isto:

# Directive
angular.module('myapp.directives')
.directive 'ActiveTab', ($route) ->
  restrict: 'A'

  link: (scope, element, attrs) ->
    klass = "active"

    if $route.current.activeTab? and attrs.flActiveLink is $route.current.activeTab
      element.addClass(klass)

    scope.$on '$routeChangeSuccess', (event, current) ->
      if current.activeTab? and attrs.flActiveLink is current.activeTab
        element.addClass(klass)
      else
        element.removeClass(klass)

# Routing
$routeProvider
.when "/page",
  templateUrl: "page.html"
  activeTab: "page"
.when "/other_page",
  templateUrl: "other_page.html"
  controller: "OtherPageCtrl"
  activeTab: "other_page"

# View (.jade)
a(ng-href='/page', active-tab='page') Page
a(ng-href='/other_page', active-tab='other_page') Other page

Não depende de URLs e, portanto, é muito fácil configurá-lo para qualquer subpágina, etc.


Se você estiver usando ngRoute (para roteamento), então seu aplicativo terá a configuração abaixo,

angular
  .module('appApp', [
    'ngRoute'
 ])
config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl',
        controllerAs: 'main'
      })
      .when('/about', {
        templateUrl: 'views/about.html',
        controller: 'AboutCtrl',
        controllerAs: 'about'
      })
}
});

Agora, basta adicionar um controlador nesta configuração como abaixo,

angular
      .module('appApp', [
        'ngRoute'
     ])
    config(function ($routeProvider) {
        $routeProvider
          .when('/', {
            templateUrl: 'views/main.html',
            controller: 'MainCtrl',
            activetab: 'main'
          })
          .when('/about', {
            templateUrl: 'views/about.html',
            controller: 'AboutCtrl',
            activetab: 'about'
          })
    }
    })
  .controller('navController', function ($scope, $route) {
    $scope.$route = $route;
  });

Como você mencionou a guia ativa em sua configuração, agora só precisa adicionar uma classe ativa à sua tag <li> ou <a> . Gostar,

ng-class="{active: $route.current.activetab == 'about'}"

O que significa que, sempre que o usuário clicar sobre a página, isso identificará automaticamente a guia atual e aplicará a classe css ativa.

Eu espero que isso ajude!


Você também pode simplesmente injetar o local no escopo e usá-lo para deduzir o estilo da navegação:

function IndexController( $scope, $rootScope, $location ) {
  $rootScope.location = $location;
  ...
}

Então use-o na sua ng-class :

<li ng-class="{active: location.path() == '/search'}">
  <a href="/search">Search><a/>
</li>

Eu concordo com a postagem de Rob sobre ter um atributo personalizado no controlador. Aparentemente eu não tenho representante suficiente para comentar. Aqui está o jsfiddle que foi solicitado:

amostra de html

<div ng-controller="MyCtrl">
    <ul>
        <li ng-repeat="link in links" ng-class="{active: $route.current.activeNav == link.type}"> <a href="{{link.uri}}">{{link.name}}</a>

        </li>
    </ul>
</div>

amostra app.js

angular.module('MyApp', []).config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/a', {
        activeNav: 'a'
    })
        .when('/a/:id', {
        activeNav: 'a'
    })
        .when('/b', {
        activeNav: 'b'
    })
        .when('/c', {
        activeNav: 'c'
    });
}])
    .controller('MyCtrl', function ($scope, $route) {
    $scope.$route = $route;
    $scope.links = [{
        uri: '#/a',
        name: 'A',
        type: 'a'
    }, {
        uri: '#/b',
        name: 'B',
        type: 'b'
    }, {
        uri: '#/c',
        name: 'C',
        type: 'c'
    }, {
        uri: '#/a/detail',
        name: 'A Detail',
        type: 'a'
    }];
});

http://jsfiddle.net/HrdR6/


Não me lembro onde encontrei esse método, mas é bem simples e funciona bem.

HTML:

<nav role="navigation">
    <ul>
        <li ui-sref-active="selected" class="inactive"><a ui-sref="tab-01">Tab 01</a></li> 
        <li ui-sref-active="selected" class="inactive"><a ui-sref="tab-02">Tab 02</a></li>
    </ul>
</nav>

CSS:

  .selected {
    background-color: $white;
    color: $light-blue;
    text-decoration: none;
    border-color: $light-grey;
  } 

Talvez uma diretiva como esta possa resolver seu problema: http://jsfiddle.net/p3ZMR/4/

HTML

<div ng-app="link">
<a href="#/one" active-link="active">One</a>
<a href="#/two" active-link="active">One</a>
<a href="#" active-link="active">home</a>


</div>

JS

angular.module('link', []).
directive('activeLink', ['$location', function(location) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs, controller) {
            var clazz = attrs.activeLink;
            var path = attrs.href;
            path = path.substring(1); //hack because path does bot return including hashbang
            scope.location = location;
            scope.$watch('location.path()', function(newPath) {
                if (path === newPath) {
                    element.addClass(clazz);
                } else {
                    element.removeClass(clazz);
                }
            });
        }

    };

}]);




angularjs