Кто-нибудь из вас знает, как хорошо обрабатывать привязку привязки привязки в AngularJS?
У меня есть следующая разметка для простой часто задаваемой страницы
<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>
При нажатии на любую из приведенных выше ссылок AngularJS перехватывает и перенаправляет меня на совершенно другую страницу (в моем случае - 404 страницы, так как нет маршрутов, соответствующих ссылкам).
Моя первая мысль заключалась в том, чтобы создать маршрут, соответствующий "/faq/: chapter", а в соответствующем контроллере проверить $routeParams.chapter после соответствующего элемента, а затем использовать jQuery для прокрутки до него. Но тогда AngularJS снова надеты на меня и просто прокручивается до верхней части страницы.
Итак, кто-нибудь здесь сделал что-то подобное в прошлом и знает хорошее решение?
Изменить: переход на html5Mode должен решить мои проблемы, но мы все равно должны поддерживать IE8 +, поэтому я боюсь, что это не принятое решение:/
Вы ищете $anchorScroll()
.
Здесь (дерьмовая) документация.
В основном вы просто вводите его и вызываете его в своем контроллере, и он будет прокручивать вас до любого элемента с идентификатором, найденным в $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: использовать это с маршрутизацией
Настройте свою маршрутизацию angular как обычно, а затем просто добавьте следующий код.
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();
});
});
и ваша ссылка будет выглядеть так:
<a href="#/test?scrollTo=foo">Test/Foo</a>
Вот Plunker, демонстрирующий прокрутку с маршрутизацией и $anchorScroll
И даже проще:
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();
});
});
и ваша ссылка будет выглядеть так:
<a href="#/test#foo">Test/Foo</a>
в моем случае я заметил, что логика маршрутизации была нажата, если я изменил хэш местоположения. следующий трюк работал.
$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);
};
Нет необходимости изменять какую-либо маршрутизацию или что-то еще, просто нужно использовать target="_self"
при создании ссылок
Пример:
<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>
И используйте атрибут id
в html:
<h3 id="faq-1">Question 1</h3>
<h3 id="faq-2">Question 2</h3>
<h3 id="faq-3">Question 3</h3>
Нет необходимости использовать ## как указано/упомянуто в комментариях; -)
<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>
Если вы всегда знаете маршрут, вы можете просто добавить якорь следующим образом:
href="#/route#anchorID
где route
- текущий маршрут angular, а anchorID
соответствует <a id="anchorID">
где-то на странице
reloadOnSearch: false
для этого маршрута в конфигурации маршрутов, angular не будет вызывать изменение маршрута и просто прокрутит до идентификатора. Я бы сказал, что в сочетании с полным маршрутом, указанным в теге a
, это самое простое и простое решение.
Это было мое решение, используя директиву, которая выглядит больше Angular -y, потому что мы имеем дело с 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>
Я использовал службу $anchorScroll. Чтобы противодействовать обновлению страницы, который сопровождается изменением хэша, я пошел вперед и отменил событие locationChangeStart. Это работало для меня, потому что у меня была страница справки, подключенная к ng-коммутатору, и обновления обновили бы приложение.
$anchorScroll
работает для этого, но есть гораздо лучший способ использовать его в более поздних версиях Angular.
Теперь $anchorScroll
принимает хеш в качестве необязательного аргумента, поэтому вам не нужно вообще менять $location.hash
. (документация)
Это лучшее решение, потому что оно вообще не влияет на маршрут. Я не мог заставить любое другое решение работать, потому что я использую ngRoute, и маршрут будет перезагружаться, как только я установлю $location.hash(id)
, прежде чем $anchorScroll
сможет сделать свою магию.
Вот как его использовать... во-первых, в директиве или контроллере:
$scope.scrollTo = function (id) {
$anchorScroll(id);
}
а затем в представлении:
<a href="" ng-click="scrollTo(id)">Text</a>
Кроме того, если вам нужно учесть фиксированный navbar (или другой пользовательский интерфейс), вы можете установить смещение для $anchorScroll как это (в функции запуска основного модуля):
.run(function ($anchorScroll) {
//this will make anchorScroll scroll to the div minus 50px
$anchorScroll.yOffset = 50;
});
id
в этом представлении.
ngRoute
и более новую версию Angular.
Это старый пост, но я долгое время изучал различные решения, поэтому хотел поделиться еще одним простым. Просто добавив target="_self"
в тег <a>
, исправил его для меня. Ссылка работает и возвращает меня в нужное место на странице.
Тем не менее, Angular все еще вводит какую-то странность С# в URL-адресе, поэтому вы можете столкнуться с трудностями с помощью кнопки "Назад" для навигации и после этого этого метода.
Я обошел это в логике маршрутов для своего приложения.
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;
}
});
}
Попробуйте установить префикс хеша для angular маршрутов $locationProvider.hashPrefix('!')
Полный пример:
angular.module('app', [])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider){
$routeProvider.when( ... );
$locationProvider.hashPrefix('!');
}
])
Это может быть новый атрибут для ngView, но мне удалось привязать хэш-ссылки для работы с angular-route
с помощью атрибута ngView autoscroll
и "double-hashes".
(Следующий код использовался с angular -strap)
<!-- 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>
Я мог бы сделать это так:
<li>
<a href="#/#about">About</a>
</li>
Моим решением с ng-route была эта простая директива:
app.directive('scrollto',
function ($anchorScroll,$location) {
return {
link: function (scope, element, attrs) {
element.click(function (e) {
e.preventDefault();
$location.hash(attrs["scrollto"]);
$anchorScroll();
});
}
};
})
html выглядит следующим образом:
<a href="" scrollTo="yourid">link</a>
scroll-to="yourid"
и scroll-to="yourid"
имя директиве scrollTo
(и обращаться к атрибуту как attrs["scrollTo"]
? Кроме того, без явного включения jQuery обработчик должен быть связан с element.on('click', function (e) {..})
.
Вот как-то грязное обходное решение, создавая настраиваемую директиву, которая будет прокручиваться до указанного элемента (с жестко закодированным "faq" )
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);
}
}
};
});
$anchorScroll
, см. Мой ответ.
Если вы не любите использовать ng-click
здесь альтернативное решение. Он использует filter
для генерации правильного URL-адреса на основе текущего состояния. В моем примере используется ui.router.
Преимущество заключается в том, что пользователь увидит, где ссылка продолжает зависать.
<a href="{{ 'my-element-id' | anchor }}">My element</a>
Фильтр:
.filter('anchor', ['$state', function($state) {
return function(id) {
return '/#' + $state.current.url + '#' + id;
};
}])
Получить прокрутку. Он также поддерживает Анимированную/Гладкую прокрутку в качестве дополнительной функции. Подробная информация для Angular Библиотека прокрутки:
Github - https://github.com/oblador/angular-scroll
Бауэр: bower install --save angular-scroll
npm: npm install --save angular-scroll
Minfied version - только 9kb
Плавная прокрутка (анимированная прокрутка) - да
Scroll Spy - да
Документация - отлично
Демо - http://oblador.github.io/angular-scroll/
Надеюсь, что это поможет.
<a href="/#/#faq-1">Question 1</a>
<a href="/#/#faq-2">Question 2</a>
<a href="/#/#faq-3">Question 3</a>
Я не уверен на 100%, если это работает все время, но в моем приложении это дает мне ожидаемое поведение.
Предположим, вы находитесь на странице О, и у вас есть следующий маршрут:
yourApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/about', {
templateUrl: 'about.html',
controller: 'AboutCtrl'
}).
otherwise({
redirectTo: '/'
});
}
]);
Теперь вы в 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>
В заключение
Включение имени страницы перед тем, как якорь помогло. Дайте мне знать о ваших мыслях.
Даунсайд
Это приведет к повторному отображению страницы, а затем прокрутку к якорю.
UPDATE
Лучше всего добавить следующее:
<a href="#tab1" onclick="return false;">First Part</a>
Вы можете попытаться использовать anchorScroll.
Таким образом, контроллер будет:
app.controller('MainCtrl', function($scope, $location, $anchorScroll, $routeParams) {
$scope.scrollTo = function(id) {
$location.hash(id);
$anchorScroll();
}
});
И представление:
<a href="" ng-click="scrollTo('foo')">Scroll to #foo</a>
... и не секрет для идентификатора привязки:
<div id="foo">
This is #foo
</div>
См. https://code.angularjs.org/1.4.10/docs/api/ngRoute/provider/ $routeProvider
[reloadOnSearch = true] - {boolean =} - перезагрузить маршрут, когда изменяются только $location.search() или $location.hash().
Установка этого значения в false сделала трюк без всего вышеперечисленного для меня.
Когда-то в angularjs приложение hash navigation не работает, а javascript-библиотеки bootstrap javascript широко используют этот тип навигации, чтобы заставить его работать, добавляя target="_self"
к тегу привязки.
например <a data-toggle="tab" href="#id_of_div_to_navigate" target="_self">
Я использую AngularJS 1.3.15 и, похоже, мне не нужно ничего особенного.
https://code.angularjs.org/1.3.15/docs/api/ng/provider/ $anchorScrollProvider
Итак, для меня в моем html работает следующее:
<ul>
<li ng-repeat="page in pages"><a ng-href="#{{'id-'+id}}">{{id}}</a>
</li>
</ul>
<div ng-attr-id="{{'id-'+id}}" </div>
Мне не пришлось вообще вносить какие-либо изменения в мой контроллер или JavaScript.
Ни одно из вышеперечисленных решений не работает для меня, но я просто попробовал это, и это сработало,
<a href="#/#faq-1">Question 1</a>
Итак, я понял, что мне нужно уведомить страницу, чтобы начать с индексной страницы, а затем использовать традиционный якорь.
В моем уме @slugslog имел это, но я бы изменил одно. Я бы использовал вместо этого замену, поэтому вам не нужно ее возвращать.
$scope.scrollTo = function(id) {
var old = $location.hash();
$location.hash(id).replace();
$anchorScroll();
};
Docs Поиск "Заменить метод"
При изменении маршрута он будет прокручиваться до верхней части страницы.
$scope.$on('$routeChangeSuccess', function () {
window.scrollTo(0, 0);
});
поместите этот код на свой контроллер.
Я пытался сделать мое приложение Angular прокруткой к загрузке якоря opon и столкнулось с правилами перезаписи URL-адреса $routeProvider.
После долгих экспериментов я остановился на этом:
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();
}
}
});
ng-href=""
.