diff --git a/src/app.js b/src/app.js index 13b9f63..d5f1505 100644 --- a/src/app.js +++ b/src/app.js @@ -13,11 +13,24 @@ navMenuButtons.render(NAV_MENU); const routerPagesContainer = new RouterPagesContainer(); -// Новые страницы обязательно добавляем тут +/** + * Добавление страниц в Роутинг выполняется на странице app.js + * @example + * routerPagesContainer.addRoutes([ + * {url: '/', pageComponent: MainPage}, + * {url: '/api', pageComponent: ApiPage}, + * ]); + */ routerPagesContainer.addRoutes([ {url: '/', pageComponent: MainPage}, {url: '/api', pageComponent: ApiPage}, ]); -// Этот метод генерит событие Route, чтобы все компоненты получили его после инициализации +/** + * Этот метод генерит событие Route, чтобы все компоненты получили его после инициализации. + * Поэтому вызывать его надо в самом конце, когда уже созданы все компоненты приложения. + * @example + * // Вызывать его можно только один раз в программе + * routeService.init(); + */ routeService.init(); diff --git a/src/components/router-pages-container/RouterPagesContainer.js b/src/components/router-pages-container/RouterPagesContainer.js index f0d9bd7..cccfe13 100644 --- a/src/components/router-pages-container/RouterPagesContainer.js +++ b/src/components/router-pages-container/RouterPagesContainer.js @@ -4,28 +4,62 @@ import NotFoundPage from '../not-found-page'; import './RouterPagesContainer.css'; +/** + * @interface Route + * @property {string} url - маршрут страницы начинается с "/" + * @property {Component} pageComponent - компонент (класс) страницы + */ + +/** + * Класс для рендера страниц при изменении роутинга + */ class RouterPagesContainer extends Component { + /** + * Список всех маршрутов + * @type {Route[]} + */ routes = []; + /** + * Текущая открытая страница + * @type {Component} + */ currentPage; + /** + * Текущий открытый url + * @type {string} + */ + url; + constructor () { super('#page-container', document.body); routeService.onChange(({url}) => { + // Если под указанный url нет pageComponent, то будет испольщована страница NotFound const {pageComponent: PageComponent = NotFoundPage} = this.routes.find((route) => { return route.url === url; }) || {}; + // Удаляет предыдущую страницу if (this.currentPage) { this.currentPage.destroy(); } - this.currentPage = new PageComponent('#page', this.mainNode); + // Рендерит новую страницу, если url изменился + if (url !== this.currentUrl) { + this.currentUrl = url; + this.currentPage = new PageComponent('#page', this.mainNode); + } }); } + /** + * Добавляет страницы в компонент, чтобы рендерить их при изменении маршрута. Рекомендуется + * все страницы передавать в app.js в одном месте + * @param {Route[]} routes - список маршрутов с компонентами + */ addRoutes = (routes) => { this.routes = this.routes.concat(routes); } diff --git a/src/services/EmitService.js b/src/services/EmitService.js index 1b1a899..ecab102 100644 --- a/src/services/EmitService.js +++ b/src/services/EmitService.js @@ -16,6 +16,11 @@ class EmitService { * Метод подписки на события компонента * @param {string} eventName - событие компонента, на которое будет реагировать обработчик * @param {Function} listener - обработчик события + * @example + * // Подписка на событие + * _.subscribe('click', () => { + * ... + * }); */ subscribe = (eventName, listener) => { const listeners = this._events[eventName] || []; @@ -29,6 +34,10 @@ class EmitService { * Метод генерирует событие * @param {string} eventName - событие, которое необходимо сгенерировать * @param {unknown[]} args - аругемнты, который необходимо передать обработчикам события + * @example + * // Сгенерировать событие. Можно передать любое количество аругментов, все эти аргументы в таком же + * // порядке будут переданы подписчикам компонента + * _.next('click', arg1, arg2, ..., argN); */ next = (eventName, ...args) => { const listeners = this._events[eventName]; diff --git a/src/services/RouteService.js b/src/services/RouteService.js index f35efb5..a4c4b18 100644 --- a/src/services/RouteService.js +++ b/src/services/RouteService.js @@ -1,9 +1,28 @@ import EmitService from './EmitService'; import {parse, stringify} from 'querystring'; +/** + * @function RouterListener + * @param {string} url - путь роута + * @param {Object} query - объект ключ-значение из url + */ + +/** + * Константа для события изменения роута + * @private + */ const ROUTE_CHANGE = 'routeChange'; +/** + * Класс для работы с роутингом. Позволяет переходить по роутам и генерит событие изменения роута. + */ class RouteService extends EmitService { + /** + * @type {boolean} - проверяет чтобы инициализация была не больше одного раза + * @private + */ + _isInit = false; + constructor () { super(); @@ -11,6 +30,10 @@ class RouteService extends EmitService { this._events[ROUTE_CHANGE] = []; } + /** + * Метод получения данных url'а + * @private + */ getUrlData = () => { return { url: location.pathname, @@ -18,14 +41,37 @@ class RouteService extends EmitService { }; } + /** + * Генерирует событие изменения роута + * @private + */ generateNext = () => { this.next(ROUTE_CHANGE, this.getUrlData()); } + /** + * Метод инициализации сервиса, чтобы сгенерить первое событие перехода для приложения + */ init = () => { - this.generateNext(); + if (!this._isInit) { + this.generateNext(); + return; + } + throw new Error('RouteService можно инициализировать только 1 раз!'); } + /** + * Метод перехода по маршрутам + * @param {string} url - принимает маршрут для перехода + * @param {query} query - объект с парами ключ-значение для url + * @example + * // Переход по заданному url + * _.goTo('/users', { + * key: 'testApi', + * author: 'Petrov', + * }); + * // Это создаст строку в url - site.ru/users?key=testApi&author=Petrov + */ goTo = (url, query) => { const stringQuery = stringify(query); const urlWithQuery = url + (stringQuery ? `?${stringQuery}` : ''); @@ -33,6 +79,15 @@ class RouteService extends EmitService { this.generateNext(); } + /** + * С помощью этого метода подписываемся на событие изменения роута. + * @param {RouterListener} listener - слушатель для события изменения роута + * @example + * // Подписка на изменение url + * _.onChange(({url, query}) => { + * ... + * }); + */ onChange = (listener) => { this.subscribe(ROUTE_CHANGE, listener); }