web-dev-qa-db-fra.com

Comment gérer les liens de hachage d'ancrage dans AngularJS

Est-ce que l'un de vous sait comment gérer correctement les liens de hachage d'ancrage dans AngularJS?

J'ai le balisage suivant pour une simple page de FAQ

<a href="#faq-1">Question 1</a>
<a href="#faq-2">Question 2</a>
<a href="#faq-3">Question 3</a>

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="fa1-3">Question 3</h3>

En cliquant sur l'un des liens ci-dessus, AngularJS m'intercepte et me dirige vers une page complètement différente (dans mon cas, une page de 404 pages, aucun itinéraire ne correspondant aux liens).

Ma première pensée a été de créer un itinéraire correspondant à "/ faq /: chapitre" et dans le contrôleur correspondant, vérifiez $routeParams.chapter après un élément correspondant, puis utilisez jQuery pour y accéder.

Mais alors AngularJS me chie encore et fait quand même défiler vers le haut de la page.

Donc, quelqu'un ici a-t-il fait quelque chose de similaire dans le passé et connaît-il une bonne solution?

Edit: Le passage à html5Mode devrait résoudre mes problèmes mais nous devons quand même supporter IE8 + de toute façon, donc je crains que ce ne soit pas une solution acceptée: /

318
Rasmus

Vous recherchez $anchorScroll() .

Voici la documentation (merdique).

et voici la source.

Fondamentalement, vous venez de l'injecter et de l'appeler dans votre contrôleur, et il vous fera défiler vers n'importe quel élément avec l'identifiant trouvé dans $location.hash()

app.controller('TestCtrl', function($scope, $location, $anchorScroll) {
   $scope.scrollTo = function(id) {
      $location.hash(id);
      $anchorScroll();
   }
});

<a ng-click="scrollTo('foo')">Foo</a>

<div id="foo">Here you are</div>

Voici un plunker à démontrer

EDIT: utiliser ceci avec le routage

Configurez votre routage angular comme d'habitude, puis ajoutez simplement le code suivant.

app.run(function($rootScope, $location, $anchorScroll, $routeParams) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    $location.hash($routeParams.scrollTo);
    $anchorScroll();  
  });
});

et votre lien ressemblerait à ceci:

<a href="#/test?scrollTo=foo">Test/Foo</a>

Voici un Plunker démontrant le défilement avec routage et $ anchorScroll

Et encore plus simple:

app.run(function($rootScope, $location, $anchorScroll) {
  //when the route is changed scroll to the proper element.
  $rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
    if($location.hash()) $anchorScroll();  
  });
});

et votre lien ressemblerait à ceci:

<a href="#/test#foo">Test/Foo</a>
373
Ben Lesh

Dans mon cas, j'ai remarqué que la logique de routage était active si je modifiais la $location.hash(). L'astuce suivante a fonctionné ..

$scope.scrollTo = function(id) {
    var old = $location.hash();
    $location.hash(id);
    $anchorScroll();
    //reset to old to keep any additional routing logic from kicking in
    $location.hash(old);
};
167
slugslog

Il n'est pas nécessaire de modifier le routage ni quoi que ce soit d'autre qui nécessite d'utiliser target="_self" lors de la création des liens.

Exemple:

<a href="#faq-1" target="_self">Question 1</a>
<a href="#faq-2" target="_self">Question 2</a>
<a href="#faq-3" target="_self">Question 3</a>

Et utilisez l'attribut id dans vos éléments html comme ceci:

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>

Il n'est pas nécessaire d'utiliser ## comme indiqué/mentionné dans les commentaires ;-)

53
<a href="##faq-1">Question 1</a>
<a href="##faq-2">Question 2</a>
<a href="##faq-3">Question 3</a>

<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>
41
lincolnge

Si vous connaissez toujours l'itinéraire, vous pouvez simplement ajouter l'ancre comme ceci:

href="#/route#anchorID

route est la route angular actuelle et anchorID correspond à un <a id="anchorID"> quelque part sur la page.

19
cab1113

Ceci a été ma solution en utilisant une directive qui semble plus angulaire-y parce que nous avons affaire au DOM:

Plnkr ici

github

CODE

angular.module('app', [])
.directive('scrollTo', function ($location, $anchorScroll) {
  return function(scope, element, attrs) {

    element.bind('click', function(event) {
        event.stopPropagation();
        var off = scope.$on('$locationChangeStart', function(ev) {
            off();
            ev.preventDefault();
        });
        var location = attrs.scrollTo;
        $location.hash(location);
        $anchorScroll();
    });

  };
});

HTML

<ul>
  <li><a href="" scroll-to="section1">Section 1</a></li>
  <li><a href="" scroll-to="section2">Section 2</a></li>
</ul>

<h1 id="section1">Hi, I'm section 1</h1>
<p>
Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. 
 Summus brains sit​​, morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris. 
Hi mindless mortuis soulless creaturas, imo evil stalking monstra adventus resi dentevil vultus comedat cerebella viventium. 
Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.
</p>

<h1 id="section2">I'm totally section 2</h1>
<p>
Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. 
 Summus brains sit​​, morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris. 
Hi mindless mortuis soulless creaturas, imo evil stalking monstra adventus resi dentevil vultus comedat cerebella viventium. 
Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.
</p>

J'ai utilisé le service $ anchorScroll. Pour contrecarrer l'actualisation de la page qui accompagne le changement de hachage, je suis allée de l'avant et j'ai annulé l'événement locationChangeStart. Cela a fonctionné pour moi parce que j'avais une page d'aide connectée à un commutateur ng et que les mises à jour casseraient essentiellement l'application.

13
KhalilRavanna

$anchorScroll fonctionne pour cela, mais il existe une bien meilleure façon de l'utiliser dans les versions plus récentes d'Angular.

Maintenant, $anchorScroll accepte le hachage comme argument facultatif, vous n'avez donc pas besoin de changer $location.hash. ( documentation )

C'est la meilleure solution car cela n'affecte en rien l'itinéraire. Je ne pouvais faire fonctionner aucune des autres solutions car j'utilisais ngRoute et la route serait rechargée dès que je définirais $location.hash(id), avant que $anchorScroll ne puisse faire sa magie.

Voici comment l'utiliser ... tout d'abord, dans la directive ou le contrôleur:

$scope.scrollTo = function (id) {
  $anchorScroll(id);  
}

et ensuite dans la vue:

<a href="" ng-click="scrollTo(id)">Text</a>

De plus, si vous devez prendre en compte une barre de navigation fixe (ou une autre interface utilisateur), vous pouvez définir le décalage pour $ anchorScroll comme suit (dans la fonction d'exécution du module principal):

.run(function ($anchorScroll) {
   //this will make anchorScroll scroll to the div minus 50px
   $anchorScroll.yOffset = 50;
});
13
Rebecca

J'ai contourné cela dans la logique de routage pour mon application.

function config($routeProvider) {
  $routeProvider
    .when('/', {
      templateUrl: '/partials/search.html',
      controller: 'ctrlMain'
    })
    .otherwise({
      // Angular interferes with anchor links, so this function preserves the
      // requested hash while still invoking the default route.
      redirectTo: function() {
        // Strips the leading '#/' from the current hash value.
        var hash = '#' + window.location.hash.replace(/^#\//g, '');
        window.location.hash = hash;
        return '/' + hash;
      }
    });
}
5
nicksanta

Essayez de définir un préfixe de hachage pour angular routes $locationProvider.hashPrefix('!')

Exemple complet:

angular.module('app', [])
  .config(['$routeProvider', '$locationProvider', 
    function($routeProvider, $locationProvider){
      $routeProvider.when( ... );
      $locationProvider.hashPrefix('!');
    }
  ])
5
Maxim Grach

Ceci est un vieux post, mais j'ai passé beaucoup de temps à chercher différentes solutions et je voulais donc en partager une plus simple. Le simple ajout de target="_self" à la balise <a> a corrigé le problème. Le lien fonctionne et m'emmène au bon endroit sur la page.

Cependant, Angular ajoute toujours un caractère étrange avec le # dans l'URL afin que vous puissiez rencontrer des problèmes lors de l'utilisation du bouton de retour pour la navigation, par exemple après l'utilisation de cette méthode.

5
Benjamin

Cela peut être un nouvel attribut pour ngView, mais j’ai réussi à l’obtenir pour que les liens de hachage d’ancrage fonctionnent avec angular-route en utilisant l’attribut ngView autoscroll et les "doubles-hachages".

ngView (voir défilement automatique)

(Le code suivant a été utilisé avec une sangle angulaire)

<!-- use the autoscroll attribute to scroll to hash on $viewContentLoaded -->    
<div ng-view="" autoscroll></div>

<!-- A.href link for bs-scrollspy from angular-strap -->
<!-- A.ngHref for autoscroll on current route without a location change -->
<ul class="nav bs-sidenav">
  <li data-target="#main-html5"><a href="#main-html5" ng-href="##main-html5">HTML5</a></li>
  <li data-target="#main-angular"><a href="#main-angular" ng-href="##main-angular" >Angular</a></li>
  <li data-target="#main-karma"><a href="#main-karma" ng-href="##main-karma">Karma</a></li>
</ul>
4
michael

Je pourrais faire ça comme ça:

<li>
<a href="#/#about">About</a>
</li>
3
Niru

Voici une solution de contournement en créant une directive personnalisée faisant défiler l'élément spécifié (avec "faq" codé en dur)

app.directive('h3', function($routeParams) {
  return {
    restrict: 'E',
    link: function(scope, element, attrs){        
        if ('faq'+$routeParams.v == attrs.id) {
          setTimeout(function() {
             window.scrollTo(0, element[0].offsetTop);
          },1);        
        }
    }
  };
});

http://plnkr.co/edit/Po37JFeP5IsNoz5ZycF?p=preview

2
Valentyn Shybanov

Ma solution avec ng-route était cette directive simple:

   app.directive('scrollto',
       function ($anchorScroll,$location) {
            return {
                link: function (scope, element, attrs) {
                    element.click(function (e) {
                        e.preventDefault();
                        $location.hash(attrs["scrollto"]);
                        $anchorScroll();
                    });
                }
            };
    })

Le html ressemble à:

<a href="" scrollTo="yourid">link</a>
2
MrFlo
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
2
felipe_dmz

Obtenez votre fonctionnalité de défilement facilement. Il prend également en charge le défilement animé/fluide en tant que fonctionnalité supplémentaire. Détails pour la bibliothèque Angular Scroll :

Github - https://github.com/oblador/angular-scroll

Bower : bower install --save angular-scroll

npm : npm install --save angular-scroll

Version réduite - seulement 9kb

Défilement lisse (défilement animé) - oui

Scroll Spy - oui

Documentation - excellente

Démo - http://oblador.github.io/angular-scroll/

J'espère que cela t'aides.

1
Akash

J'essayais de faire défiler mon application Angular vers un point d'ancrage lors du chargement et de me familiariser avec les règles de réécriture d'URL de $ routeProvider.

Après une longue expérimentation, je me suis installé sur ceci:

  1. enregistrez un gestionnaire d'événements document.onload à partir de la section .run () du module d'application Angular.
  2. dans le gestionnaire, recherchez ce que la balise d'ancrage d'origine était censée être en effectuant des opérations sur les chaînes.
  3. écrasez location.hash avec la balise d'ancrage réduite (ce qui force $ routeProvider à l'écraser immédiatement avec sa règle "# /". Mais c'est correct, car Angular est maintenant synchronisé avec ce qui se passe. dans l'URL 4) appelez $ anchorScroll ().
angular.module("bla",[]).}])
.run(function($location, $anchorScroll){
         $(document).ready(function() {
         if(location.hash && location.hash.length>=1)                {
                        var path = location.hash;
                        var potentialAnchor = path.substring(path.lastIndexOf("/")+1);
                        if ($("#" + potentialAnchor).length > 0) {   // make sure this hashtag exists in the doc.                          
                            location.hash = potentialAnchor;
                            $anchorScroll();
                        }
                }        
 });
1
Stoyan Kenderov

Si vous n'aimez pas utiliser ng-click, voici une solution alternative. Il utilise un filter pour générer l'URL correcte en fonction de l'état actuel. Mon exemple utilise ui.router .

L'avantage est que l'utilisateur verra où le lien passe au survol.

<a href="{{ 'my-element-id' | anchor }}">My element</a>

Le filtre:

.filter('anchor', ['$state', function($state) {
    return function(id) {
        return '/#' + $state.current.url + '#' + id;
    };
}])
1
Reimund

Je ne suis pas sûr à 100% si cela fonctionne tout le temps, mais dans mon application, cela me donne le comportement attendu.

Disons que vous êtes sur la page À PROPOS DE et que vous avez l'itinéraire suivant:

yourApp.config(['$routeProvider', 
    function($routeProvider) {
        $routeProvider.
            when('/about', {
                templateUrl: 'about.html',
                controller: 'AboutCtrl'
            }).
            otherwise({
                redirectTo: '/'
            });
        }
]);

Maintenant, en vous HTML

<ul>
    <li><a href="#/about#tab1">First Part</a></li>
    <li><a href="#/about#tab2">Second Part</a></li>
    <li><a href="#/about#tab3">Third Part</a></li>                      
</ul>

<div id="tab1">1</div>
<div id="tab2">2</div>
<div id="tab3">3</div>

En conclusion

Y compris le nom de la page avant que l’ancre ne me soit utile. Faites-moi savoir à propos de vos pensées.

Inconvénient

Cela rendra à nouveau la page, puis fera défiler jusqu'à l'ancre.

UPDATE

Un meilleur moyen est d'ajouter ce qui suit:

<a href="#tab1" onclick="return false;">First Part</a>
1
Brian

Vous pouvez essayer d'utiliser anchorScroll .

Exemple

Donc, le contrôleur serait:

app.controller('MainCtrl', function($scope, $location, $anchorScroll, $routeParams) {
  $scope.scrollTo = function(id) {
     $location.hash(id);
     $anchorScroll();
  }
});

Et la vue:

<a href="" ng-click="scrollTo('foo')">Scroll to #foo</a>

... et pas de secret pour l'identifiant d'ancre:

<div id="foo">
  This is #foo
</div>
1
Edmar Miyake

Basé sur @Stoyan, j'ai proposé la solution suivante:

app.run(function($location, $anchorScroll){
    var uri = window.location.href;

    if(uri.length >= 4){

        var parts = uri.split('#!#');
        if(parts.length > 1){
            var anchor = parts[parts.length -1];
            $location.hash(anchor);
            $anchorScroll();
        }
    }
});
1
ThatMSG

Aucune des solutions ci-dessus ne fonctionne pour moi, mais je viens d'essayer cela, et cela a fonctionné,

<a href="#/#faq-1">Question 1</a>

J'ai donc réalisé que je devais notifier la page pour qu'elle commence avec la page d'index, puis utiliser l'ancre traditionnelle.

0
windmaomao

Lors du changement de route, il fera défiler vers le haut de la page.

 $scope.$on('$routeChangeSuccess', function () {
      window.scrollTo(0, 0);
  });

mettez ce code sur votre contrôleur.

0
Praveen M P

Parfois, dans angularjs, la navigation par hachage des applications ne fonctionne pas et bootstrap Les bibliothèques jquascript jquery font un usage intensif de ce type de navigation. Pour que cela fonctionne, ajoutez target="_self" à la balise d'ancrage. par exemple. <a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">

0
Kulbhushan Chaskar

Dans mon esprit, @slugslog l'avait, mais je changerais une chose. J'utiliserais plutôt remplacer pour que vous n'ayez pas à le remettre en place.

$scope.scrollTo = function(id) {
    var old = $location.hash();
    $location.hash(id).replace();
    $anchorScroll();
};

Docs Recherche de "méthode de remplacement"

0
Jackie

J'utilise AngularJS 1.3.15 et il semble que je n'ai rien de spécial à faire.

https://code.angularjs.org/1.3.15/docs/api/ng/provider/ $ anchorScrollProvider

Donc, ce qui suit fonctionne pour moi dans mon HTML:

<ul>
  <li ng-repeat="page in pages"><a ng-href="#{{'id-'+id}}">{{id}}</a>
  </li>
</ul>
<div ng-attr-id="{{'id-'+id}}" </div>

Je n'ai pas du tout dû modifier mon contrôleur ou JavaScript.

0
Digant C Kasundra

Voir https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $ routeProvider

[reloadOnSearch = true] - {boolean =} - permet de recharger la route lorsque seuls $ location.search () ou $ location.hash () sont modifiés.

Définir ce paramètre sur false a permis de résoudre le problème sans tout ce qui précède.

0
geekdenz