В этом посте я создам очень простой Angular module
, который даст вашему одностраничному приложению возможность всегда прокручивать страницу вверх.
Сначала давайте разберемся, что вы ожидаете от этого плагина:
Когда я прокручиваю страницу до конца, а затем перехожу к другой, я ожидаю, что позиция прокрутки страницы будет вверху на новой странице.
Когда я прокручиваю страницу назад, я ожидаю, что она запомнит, где я остановился, и прокрутит страницу до этой позиции.
Ладно, начнем, а?
Чтобы мы могли сохранить предыдущую позицию прокрутки, нам нужно хранилище для сохранения позиции, я предпочитаю использовать собственный sessionStorage
.
Поэтому я создам сервис по этой причине и назову его SessionStorageService
.
@Injectable() export class SessionStorageService { write ( key : string, value : any ) { if ( value ) { value = JSON.stringify( value ); } sessionStorage.setItem( key, value ); } read<T> ( key : string ) : T { let value : string = sessionStorage.getItem( key ); if ( value && value != "undefined" && value != "null" ) { return <T>JSON.parse( value ); } } }
Довольно просто, у него есть два метода, write
и read
.
Теперь мы можем приступить к созданию нашего ScrollStore
.
@Injectable() export class ScrollStore { constructor ( private router : Router, private storageService : SessionStorageService ) { } }
Как видите, я внедрил маршрутизатор, потому что использование этого плагина имеет смысл только в том случае, если у вас многостраничное приложение, поэтому вы уже должны быть знакомы с маршрутизатором и импортировать его модуль в свое приложение.
Теперь, когда у меня есть маршрутизатор, я подпишусь на его событие, и каждый раз, когда происходит навигация по странице, я вызываю свою функцию для прокрутки вверх страницы или получения предыдущей позиции:
subscribeToRouter () { this.router.events.subscribe( event => { if ( event instanceof NavigationStart ) { this.saveScrollPos( this.currentUrl ); } if ( event instanceof NavigationEnd ) { this.retrieveScrollPos( event ); } } ); }
Как видите, я подписался на события router.events
, и каждая навигация будет давать мне событие, которое может быть событием NavigationStart
или NavigationEnd
.
Есть еще пара других, которые вы можете найти здесь.
Итак, теперь давайте обновим consructor
для вызова этой функции в сервисе:
@Injectable() export class ScrollStore { constructor ( private router : Router, private storageService : SessionStorageService ) { this.subscribeToRouter(); }
Хорошо, теперь давайте посмотрим, что это за два метода:
Когда мы начинаем навигацию, мы всегда хотим сохранить текущую позицию прокрутки с текущим URL-адресом, поэтому, если мы сразу вернемся на эту страницу, мы сможем получить ее и прокрутить до предыдущей позиции.
private saveScrollPos ( url ) { this.storageService.write( url, this.scrollTop ); }
Обратите внимание, что я передаю currentUrl
в saveScrollPos
, давайте посмотрим, что это может быть:
currentUrl
— это простой метод получения, который всегда возвращает текущий URL-адрес, который нам даст маршрутизатор:
private get currentUrl () { return this.router.url; }
Теперь, когда мы сохранили текущую позицию прокрутки для текущего URL-адреса, давайте поработаем над извлечением.
Что нам нужно, так это в конце перехода на страницу проверить, есть ли сохраненная позиция прокрутки против нового URL-адреса, и если она есть, прокрутить до нее, в противном случае прокрутить до нуля (вверху страницы).
private retrieveScrollPos ( event : NavigationStart ) { let retrievedScrollPos = this.storageService.read( event.url ); if ( retrievedScrollPos === undefined ) { console.log( 'No saved position for ' + event.url + ' scroll to zero instead' ); this.scrollToZero(); } else { console.log( 'Postion found for ' + event.url + ' scroll to' + retrievedScrollPos ); this.scrollTo( retrievedScrollPos as number ); } }
Что происходит, когда вызывается retrieveScrollPos
, я проверяю наш storageService
, чтобы увидеть, есть ли позиция, сохраненная по URL-адресу, если есть, я говорю scrollTo(retreivedScrollPos)
, и если нет, значит, мы должны прокрутить вверх, что я позвони scrollToZero
.
Вот эти два метода:
private scrollTo ( retrievedScrollPos : number ) { this.scrollTop = retrievedScrollPos; } private scrollToZero () { this.scrollTop = 0; }
А вот scrollTop
, у которого есть геттер и сеттер для document.body.scrollTop
private get scrollTop () { return document.body.scrollTop; } private set scrollTop ( number : number ) { document.body.scrollTop = number; }
Вот и все, и вот сервис в сборе:
@Injectable() export class ScrollStore { constructor ( private router : Router, private storageService : SessionStorageService ) { this.subscribeToRouter(); } subscribeToRouter () { this.router.events.subscribe( event => { if ( event instanceof NavigationStart ) { this.saveScrollPos( this.currentUrl ); } if ( event instanceof NavigationEnd ) { this.retrieveScrollPos( event ); } } ); } private scrollToZero () { this.scrollTop = 0; } private get currentUrl () { return this.router.url; } private get scrollTop () { return document.body.scrollTop; } private set scrollTop ( number : number ) { document.body.scrollTop = number; } private saveScrollPos ( url ) { this.storageService.write( url, this.scrollTop ); } private retrieveScrollPos ( event : NavigationStart ) { let retrievedScrollPos = this.storageService.read( event.url ); if ( retrievedScrollPos === undefined ) { console.log( 'No saved position for ' + event.url + ' scroll to zero instead' ); this.scrollToZero(); } else { console.log( 'Postion found for ' + event.url + ' scroll to' + retrievedScrollPos ); this.scrollTo( retrievedScrollPos as number ); } } private scrollTo ( retrievedScrollPos : number ) { this.scrollTop = retrievedScrollPos; } }
И последнее, но не менее важное: нам нужно отправить этот сервис внутри модуля, чтобы сделать его подключаемым:
@NgModule( { providers : [ SessionStorageService, ScrollStore ] } ) export class ScrollStoreModule { constructor ( private scrollStore : ScrollStore ) { } }
Внедрение ScrollStore
внутрь ScrollStoreModule
является наиболее важной частью здесь, что позволяет создать экземпляр службы и начать работать.
Вот страница github, не стесняйтесь вносить свой вклад.