From 3e7134045dd701ffb1391e8743400c8b0f050a28 Mon Sep 17 00:00:00 2001 From: Nikolay <46225163+vigdorov@users.noreply.github.com> Date: Fri, 24 Jul 2020 14:28:20 +0300 Subject: [PATCH] =?UTF-8?q?HM-62.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=B0=D0=B3=D0=B0=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=20=D0=BC=D0=B5=D0=B6=D0=B4=D1=83=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86=D0=B0=D0=BC=D0=B8=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/component/Component.js | 23 +++++++- src/components/logs-page/LogsPage.js | 33 ++++++----- src/components/pagination/Pagination.js | 13 +---- .../RouterPagesContainer.js | 4 +- src/consts.js | 5 ++ src/services/EmitService.js | 43 ++++++++++++++- src/services/RouteService.js | 55 ++++++++----------- 7 files changed, 112 insertions(+), 64 deletions(-) diff --git a/src/components/component/Component.js b/src/components/component/Component.js index e59fdac..1d07493 100644 --- a/src/components/component/Component.js +++ b/src/components/component/Component.js @@ -12,6 +12,12 @@ class Component extends EmitService { */ _listeners; + /** + * Отображает текущее состояние компонента жив он или мертв + * @type {Boolean} - текущее состояние компонента + */ + _isAlive; + /** * Корневой элемент компонента * @type {Node} - корневой элемент компонента @@ -31,6 +37,7 @@ class Component extends EmitService { } this._listeners = []; this._events = {}; + this._isAlive = true; } /** @@ -51,16 +58,28 @@ class Component extends EmitService { this._listeners.forEach(({element, eventName, listener}) => { element.removeEventListener(eventName, listener); }); + this._listeners = []; + } + + /** + * Позволяет запускать рендер только в том случае, если компонент жив + * @param {Function} renderFunction - функция рендера + */ + render (renderFunction) { + if (this._isAlive) { + renderFunction(); + } } /** * Метод уничтожения компонента. Удаляет элемент из верстки, снимает обработчики и очищает подписки */ destroy = () => { + this._isAlive = false; this.clearListeners(); - this.mainNode.remove(); - this._listeners = []; this.clearSubscribes(); + this.clearEvents(); + this.mainNode.remove(); } } diff --git a/src/components/logs-page/LogsPage.js b/src/components/logs-page/LogsPage.js index dd2db11..d1f4a31 100644 --- a/src/components/logs-page/LogsPage.js +++ b/src/components/logs-page/LogsPage.js @@ -5,7 +5,7 @@ import Pagination from '../pagination'; import LogsFilters from '../logs-filters'; import routeService from '../../services/RouteService'; import {prepareToNumber} from '../../utils/urlUtils'; -import {LOG_TYPE, LOG_COLS} from '../../consts'; +import {LOG_TYPE, LOG_COLS, EVENTS} from '../../consts'; import {createElement, markText} from '../../utils/elementUtils'; const ELEMENTS_ON_PAGE = 15; @@ -36,16 +36,19 @@ class LogsPage extends Component { this.pagination = new Pagination(this.footer); - routeService.onChange(this.renderTable); + this.logList = { + [LOG_TYPE.SERVER]: [], + [LOG_TYPE.CLIENT]: [], + }; + + this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, this.renderTable); this.initPage(); } initPage = async () => { - this.logList = { - [LOG_TYPE.SERVER]: await storageLogsApi.requestServerLogs(), - [LOG_TYPE.CLIENT]: await storageLogsApi.requestClientLogs(), - }; + this.logList[LOG_TYPE.SERVER] = await storageLogsApi.requestServerLogs(); + this.logList[LOG_TYPE.CLIENT] = await storageLogsApi.requestClientLogs(); this.renderTable(); } @@ -84,14 +87,16 @@ class LogsPage extends Component { } renderTable = () => { - const {query} = routeService.getUrlData(); - const {tableType, ...omitQuery} = query; - this.table?.mainNode?.remove(); - this.table = this.tables[tableType]; - this.body.appendChild(this.table.mainNode); - const filteredRows = this.filterRows(this.logList[tableType], omitQuery); - const rows = this.cutPagginationPage(filteredRows, query.pageNumber); - this.table.render(rows); + this.render(() => { + const {query} = routeService.getUrlData(); + const {tableType, ...omitQuery} = query; + this.table?.mainNode?.remove(); + this.table = this.tables[tableType]; + this.body.appendChild(this.table.mainNode); + const filteredRows = this.filterRows(this.logList[tableType], omitQuery); + const rows = this.cutPagginationPage(filteredRows, query.pageNumber); + this.table.render(rows); + }); } } diff --git a/src/components/pagination/Pagination.js b/src/components/pagination/Pagination.js index 95426d6..d9e8c0b 100644 --- a/src/components/pagination/Pagination.js +++ b/src/components/pagination/Pagination.js @@ -2,12 +2,11 @@ import Component from '../component/index'; import routeService from '../../services/RouteService'; import {createElement} from '../../utils/elementUtils'; import {prepareToNumber} from '../../utils/urlUtils'; +import {EVENTS} from '../../consts'; const LEFT_ICON = '«'; const RIGHT_ICON = '»'; -const CHAGE_PAGE = 'changePage'; - class Pagination extends Component { buttons = []; @@ -19,9 +18,7 @@ class Pagination extends Component { this.container = this.mainNode.querySelector('.pagination'); - routeService.onChange(() => { - this.renderButtons(); - }); + this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, this.renderButtons); } renderOneButton = (text, isDisabled = false) => { @@ -99,12 +96,6 @@ class Pagination extends Component { } this.renderOneButton(RIGHT_ICON, this.currentPage >= this.countPages); - - this.next(CHAGE_PAGE, this.currentPage); - } - - onPageChange = (listener) => { - this.subscribe(CHAGE_PAGE, listener); } } diff --git a/src/components/router-pages-container/RouterPagesContainer.js b/src/components/router-pages-container/RouterPagesContainer.js index 273d4e7..a46328c 100644 --- a/src/components/router-pages-container/RouterPagesContainer.js +++ b/src/components/router-pages-container/RouterPagesContainer.js @@ -3,6 +3,7 @@ import routeService from '../../services/RouteService'; import NotFoundPage from '../not-found-page'; import './RouterPagesContainer.css'; +import {EVENTS} from '../../consts'; /** * @interface Route @@ -35,7 +36,7 @@ class RouterPagesContainer extends Component { constructor () { super('#page-container', document.body); - routeService.onChange(({url}) => { + this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, ({url}) => { // Если под указанный url нет pageComponent, то будет испольщована страница NotFound const {pageComponent: PageComponent = NotFoundPage} = this.routes.find((route) => { return route.url === url; @@ -52,7 +53,6 @@ class RouterPagesContainer extends Component { this.currentPage = new PageComponent('#page', this.mainNode); } }); - } /** diff --git a/src/consts.js b/src/consts.js index 4122460..9fb97ed 100644 --- a/src/consts.js +++ b/src/consts.js @@ -29,3 +29,8 @@ export const LOG_COLS = { [LOG_TYPE.SERVER]: SERVER_COLS, [LOG_TYPE.CLIENT]: CLIENT_COLS, }; + +export const EVENTS = { + ROUTE_CHANGE: 'routeChange', + ROUTE_SEARCH_CHANGE: 'routeSearchChange' +}; diff --git a/src/services/EmitService.js b/src/services/EmitService.js index 8032bb2..011a2b6 100644 --- a/src/services/EmitService.js +++ b/src/services/EmitService.js @@ -8,13 +8,19 @@ class EmitService { */ _events; + /** + * Подписки на события других Емитеров + */ + _subscribes; + constructor () { this._events = {}; + this._subscribes = []; } /** - * Метод подписки на события компонента - * @param {string} eventName - событие компонента, на которое будет реагировать обработчик + * Метод подписки на события текущего Емитера + * @param {string} eventName - событие Емитера, на которое будет реагировать обработчик * @param {Function} listener - обработчик события * @example * // Подписка на событие @@ -30,6 +36,16 @@ class EmitService { ]; } + /** + * Метод отписки от события текущего Емитера + * @param {string} eventName - событие Емитера, от которого нужно отписатся + * @param {Function} listener - обработчик события, нужно передать ту же функцию, что и при добавлении + */ + unsubscribe = (eventName, listener) => { + const listeners = this._events[eventName] || []; + this._events[eventName] = listeners.filter((innerListener) => innerListener !== listener); + } + /** * Метод генерирует событие * @param {string} eventName - событие, которое необходимо сгенерировать @@ -47,9 +63,30 @@ class EmitService { } /** - * Очищает все события и слушателей + * Подписка на событие другого Емитера + * @param {EventService} component - экземпляр Емитера на который нужно подписаться + * @param {string} eventName - событие на которое нужно подписаться + * @param {Function} listener - подписчик + */ + addSubscribe = (emiter, eventName, listener) => { + emiter.subscribe(eventName, listener); + this._subscribes.push({emiter, eventName, listener}); + } + + /** + * Очищает все подписки на другие Емитеры */ clearSubscribes = () => { + this._subscribes.forEach(({emiter, eventName, listener}) => { + emiter.unsubscribe(eventName, listener); + }); + this._subscribes = []; + } + + /** + * Очищает все события и слушателей + */ + clearEvents = () => { this._events = {}; } } diff --git a/src/services/RouteService.js b/src/services/RouteService.js index d4824f8..51ce18a 100644 --- a/src/services/RouteService.js +++ b/src/services/RouteService.js @@ -1,6 +1,7 @@ import EmitService from './EmitService'; import {parse} from 'querystring'; import {makeUrlWithQuery} from '../utils/urlUtils'; +import {EVENTS} from '../consts'; /** * @function RouterListener @@ -8,12 +9,6 @@ import {makeUrlWithQuery} from '../utils/urlUtils'; * @param {Object} query - объект ключ-значение из url */ -/** - * Константа для события изменения роута - * @private - */ -const ROUTE_CHANGE = 'routeChange'; - /** * Класс для работы с роутингом. Позволяет переходить по роутам и генерит событие изменения роута. */ @@ -28,7 +23,19 @@ class RouteService extends EmitService { super(); this.history = window.history; - this._events[ROUTE_CHANGE] = []; + this._events[EVENTS.ROUTE_CHANGE] = []; + } + + _nextUrl = (url, query = {}) => { + const prepareQuery = Object.entries(query) + .reduce((memo, [key, value]) => { + if (value) { + memo[key] = value; + } + return memo; + }, {}); + const urlWithQuery = makeUrlWithQuery(url, prepareQuery); + this.history.pushState({}, '', urlWithQuery); } /** @@ -47,7 +54,11 @@ class RouteService extends EmitService { * @private */ generateNext = () => { - this.next(ROUTE_CHANGE, this.getUrlData()); + this.next(EVENTS.ROUTE_CHANGE, this.getUrlData()); + } + + generatePushQuery = () => { + this.next(EVENTS.ROUTE_SEARCH_CHANGE, this.getUrlData()); } /** @@ -73,16 +84,8 @@ class RouteService extends EmitService { * }); * // Это создаст строку в url - site.ru/users?key=testApi&author=Petrov */ - goTo = (url, query = {}) => { - const prepareQuery = Object.entries(query) - .reduce((memo, [key, value]) => { - if (value) { - memo[key] = value; - } - return memo; - }, {}); - const urlWithQuery = makeUrlWithQuery(url, prepareQuery); - this.history.pushState({}, '', urlWithQuery); + goTo = (url, query) => { + this._nextUrl(url, query); this.generateNext(); } @@ -94,23 +97,11 @@ class RouteService extends EmitService { */ pushQuery = (newQuery, isClear = false) => { const {url, query} = this.getUrlData(); - this.goTo(url, { + this._nextUrl(url, { ...(isClear ? {} : query), ...newQuery, }); - } - - /** - * С помощью этого метода подписываемся на событие изменения роута. - * @param {RouterListener} listener - слушатель для события изменения роута - * @example - * // Подписка на изменение url - * _.onChange(({url, query}) => { - * ... - * }); - */ - onChange = (listener) => { - this.subscribe(ROUTE_CHANGE, listener); + this.generatePushQuery(); } }