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: /
Vous recherchez $anchorScroll()
.
Voici la documentation (merdique).
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>
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>
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);
};
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 ;-)
<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>
Si vous connaissez toujours l'itinéraire, vous pouvez simplement ajouter l'ancre comme ceci:
href="#/route#anchorID
où route
est la route angular actuelle et anchorID
correspond à un <a id="anchorID">
quelque part sur la page.
Ceci a été ma solution en utilisant une directive qui semble plus angulaire-y parce que nous avons affaire au DOM:
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();
});
};
});
<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.
$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;
});
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;
}
});
}
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('!');
}
])
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.
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>
Je pourrais faire ça comme ça:
<li>
<a href="#/#about">About</a>
</li>
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);
}
}
};
});
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>
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
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.
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:
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();
}
}
});
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;
};
}])
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>
Vous pouvez essayer d'utiliser anchorScroll .
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>
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();
}
}
});
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.
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.
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">
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"
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.
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.