twitter-bootstrap - strap - scrollspy jquery
¿Cómo implementar un scrollspy en angular.js de la manera correcta? (4)
Hay algunos desafíos al usar Scrollspy dentro de Angular (probablemente por qué AngularUI aún no incluye Scrollspy en agosto de 2013).
Debe actualizar la scrollspy cada vez que realice cambios en los contenidos DOM.
Esto se puede hacer mirando $ el elemento y estableciendo un tiempo de espera para la llamada scrollspy (''refresh'').
También debe anular el comportamiento predeterminado de los elementos de navegación si desea poder usar los elementos de navegación para navegar por el área desplazable.
Esto se puede lograr utilizando preventDefault en los elementos de anclaje para evitar la navegación y adjuntando una función scrollTo a ng-click.
Lancé un ejemplo funcional en Plunker: http://plnkr.co/edit/R0a4nJi3tBUBsluBJUo2?p=preview
¿Alguien tiene alguna idea sobre la mejor manera de implementar una scrollspy en una aplicación angular? Comencé con la implementación de Twitter Bootstrap, pero estoy luchando por encontrar un buen lugar para inicializar (y lo que es más importante actualizar después de cambios DOM) el complemento.
Básicamente, mi controlador hace una solicitud asíncrona de datos cuando está cargada y no puede procesar completamente el DOM hasta que la solicitud regrese. Luego, cuando el DOM está completamente renderizado, el plugin scrollspy necesita ser actualizado. En este momento, mi controlador emitió un evento sobre su alcance una vez que se han recibido los datos necesarios y tengo una directiva que recoge este evento. Realmente no me gusta esta solución por dos razones
Simplemente se siente hacky.
Cuando actualizo el plugin scrollspy después de recibir este evento, todavía es demasiado temprano ya que el DOM no se actualiza hasta más tarde en el ciclo. Probé evalAsync pero al final tuve que usar un tiempo de espera y solo espero que el DOM lo haga lo suficientemente rápido.
Eché un vistazo a la fuente del complemento Bootstrap y parece bastante sencillo implementar esto desde cero. El problema que estaba teniendo con eso es que cuando traté de hacer una directiva para él, no parecía poder suscribir eventos de desplazamiento desde el elemento que recibí en la función de enlace.
¿Alguien ha encontrado una buena solución para algo como esto, o alguien tiene alguna sugerencia? No estoy de ninguna manera vinculado al uso de la implementación de Bootstrap, a partir de ahora el único dep que tengo en Bootstrap es el scrollspy-plugin que acabo de agregar.
Alexander Hill hizo una publicación describiendo una implementación AngularJS de ScrollSpy de Bootstrap: http://alxhill.com/blog/articles/angular-scrollspy/
He traducido su código de CoffeeScript a JavaScript, he reparado algunos errores, he añadido algunas comprobaciones de seguridad y una función extra para una buena medida. Aquí está el código:
app.directive(''scrollSpy'', function ($window) {
return {
restrict: ''A'',
controller: function ($scope) {
$scope.spies = [];
this.addSpy = function (spyObj) {
$scope.spies.push(spyObj);
};
},
link: function (scope, elem, attrs) {
var spyElems;
spyElems = [];
scope.$watch(''spies'', function (spies) {
var spy, _i, _len, _results;
_results = [];
for (_i = 0, _len = spies.length; _i < _len; _i++) {
spy = spies[_i];
if (spyElems[spy.id] == null) {
_results.push(spyElems[spy.id] = elem.find(''#'' + spy.id));
}
}
return _results;
});
$($window).scroll(function () {
var highlightSpy, pos, spy, _i, _len, _ref;
highlightSpy = null;
_ref = scope.spies;
// cycle through `spy` elements to find which to highlight
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
spy = _ref[_i];
spy.out();
// catch case where a `spy` does not have an associated `id` anchor
if (spyElems[spy.id].offset() === undefined) {
continue;
}
if ((pos = spyElems[spy.id].offset().top) - $window.scrollY <= 0) {
// the window has been scrolled past the top of a spy element
spy.pos = pos;
if (highlightSpy == null) {
highlightSpy = spy;
}
if (highlightSpy.pos < spy.pos) {
highlightSpy = spy;
}
}
}
// select the last `spy` if the scrollbar is at the bottom of the page
if ($(window).scrollTop() + $(window).height() >= $(document).height()) {
spy.pos = pos;
highlightSpy = spy;
}
return highlightSpy != null ? highlightSpy["in"]() : void 0;
});
}
};
});
app.directive(''spy'', function ($location, $anchorScroll) {
return {
restrict: "A",
require: "^scrollSpy",
link: function(scope, elem, attrs, affix) {
elem.click(function () {
$location.hash(attrs.spy);
$anchorScroll();
});
affix.addSpy({
id: attrs.spy,
in: function() {
elem.addClass(''active'');
},
out: function() {
elem.removeClass(''active'');
}
});
}
};
});
Un fragmento de HTML, de la publicación de blog de Alexander, que muestra cómo implementar la directiva:
<div class="row" scroll-spy>
<div class="col-md-3 sidebar">
<ul>
<li spy="overview">Overview</li>
<li spy="main">Main Content</li>
<li spy="summary">Summary</li>
<li spy="links">Other Links</li>
</ul>
</div>
<div class="col-md-9 content">
<h3 id="overview">Overview</h3>
<!-- overview goes here -->
<h3 id="main">Main Body</h3>
<!-- main content goes here -->
<h3 id="summary">Summary</h3>
<!-- summary goes here -->
<h3 id="links">Other Links</h3>
<!-- other links go here -->
</div>
</div>
Encontré un proyecto en github que hace el truco añadiendo scrollspy, scrollTo y scroll animados.
https://github.com/oblador/angular-scroll
Aquí puedes ver la demostración en vivo y aquí está la fuente de ejemplo
Puedes usar bower para instalarlo
$ bower install angular-scroll
Estoy usando Scrollspy original de Bootstrap . Los href de los enlaces de navegación son como que la página no tiene ngRoute de Anuglar. Luego configure ngRoute para esa página con la opción reloadOnSearch: false
Para el desplazamiento estoy usando Angular build-in # support - si url looklikes http://host/#/path#anchor
entonces ngRoute cargará la página para el especificado ''/ ruta ''y se desplazará a'' ancla ''especificada.
No lo ves porque sin reloadOnSearch: la página falsa se carga cada vez y en el momento de desplazarse, el elemento con id como se especifica en el ancla no se representa aún. Para resolver el problema del desplazamiento inicial al anclaje después de la carga de la página inicial y el renderizado se escribe mucho.
El elemento final de mi solución es escribir mi propia directiva que se coloca en cada enlace de navegación: detiene la propagación de clics, evita la acción predeterminada y luego establece el anclaje de página para que se corresponda con los requisitos angulares. Por ejemplo, si la URL de la página es http://host/#/path
y el enlace cliqueado tiene href = "# some-id", mi directiva creará una nueva URL http://host/#/path#some-id