HM-62. Исправление бага при переходе между страницами (#28)
This commit is contained in:
@ -12,6 +12,12 @@ class Component extends EmitService {
|
|||||||
*/
|
*/
|
||||||
_listeners;
|
_listeners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отображает текущее состояние компонента жив он или мертв
|
||||||
|
* @type {Boolean} - текущее состояние компонента
|
||||||
|
*/
|
||||||
|
_isAlive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Корневой элемент компонента
|
* Корневой элемент компонента
|
||||||
* @type {Node} - корневой элемент компонента
|
* @type {Node} - корневой элемент компонента
|
||||||
@ -31,6 +37,7 @@ class Component extends EmitService {
|
|||||||
}
|
}
|
||||||
this._listeners = [];
|
this._listeners = [];
|
||||||
this._events = {};
|
this._events = {};
|
||||||
|
this._isAlive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,16 +58,28 @@ class Component extends EmitService {
|
|||||||
this._listeners.forEach(({element, eventName, listener}) => {
|
this._listeners.forEach(({element, eventName, listener}) => {
|
||||||
element.removeEventListener(eventName, listener);
|
element.removeEventListener(eventName, listener);
|
||||||
});
|
});
|
||||||
|
this._listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Позволяет запускать рендер только в том случае, если компонент жив
|
||||||
|
* @param {Function} renderFunction - функция рендера
|
||||||
|
*/
|
||||||
|
render (renderFunction) {
|
||||||
|
if (this._isAlive) {
|
||||||
|
renderFunction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Метод уничтожения компонента. Удаляет элемент из верстки, снимает обработчики и очищает подписки
|
* Метод уничтожения компонента. Удаляет элемент из верстки, снимает обработчики и очищает подписки
|
||||||
*/
|
*/
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
|
this._isAlive = false;
|
||||||
this.clearListeners();
|
this.clearListeners();
|
||||||
this.mainNode.remove();
|
|
||||||
this._listeners = [];
|
|
||||||
this.clearSubscribes();
|
this.clearSubscribes();
|
||||||
|
this.clearEvents();
|
||||||
|
this.mainNode.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Pagination from '../pagination';
|
|||||||
import LogsFilters from '../logs-filters';
|
import LogsFilters from '../logs-filters';
|
||||||
import routeService from '../../services/RouteService';
|
import routeService from '../../services/RouteService';
|
||||||
import {prepareToNumber} from '../../utils/urlUtils';
|
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';
|
import {createElement, markText} from '../../utils/elementUtils';
|
||||||
|
|
||||||
const ELEMENTS_ON_PAGE = 15;
|
const ELEMENTS_ON_PAGE = 15;
|
||||||
@ -36,16 +36,19 @@ class LogsPage extends Component {
|
|||||||
|
|
||||||
this.pagination = new Pagination(this.footer);
|
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();
|
this.initPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
initPage = async () => {
|
initPage = async () => {
|
||||||
this.logList = {
|
this.logList[LOG_TYPE.SERVER] = await storageLogsApi.requestServerLogs();
|
||||||
[LOG_TYPE.SERVER]: await storageLogsApi.requestServerLogs(),
|
this.logList[LOG_TYPE.CLIENT] = await storageLogsApi.requestClientLogs();
|
||||||
[LOG_TYPE.CLIENT]: await storageLogsApi.requestClientLogs(),
|
|
||||||
};
|
|
||||||
this.renderTable();
|
this.renderTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,14 +87,16 @@ class LogsPage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTable = () => {
|
renderTable = () => {
|
||||||
const {query} = routeService.getUrlData();
|
this.render(() => {
|
||||||
const {tableType, ...omitQuery} = query;
|
const {query} = routeService.getUrlData();
|
||||||
this.table?.mainNode?.remove();
|
const {tableType, ...omitQuery} = query;
|
||||||
this.table = this.tables[tableType];
|
this.table?.mainNode?.remove();
|
||||||
this.body.appendChild(this.table.mainNode);
|
this.table = this.tables[tableType];
|
||||||
const filteredRows = this.filterRows(this.logList[tableType], omitQuery);
|
this.body.appendChild(this.table.mainNode);
|
||||||
const rows = this.cutPagginationPage(filteredRows, query.pageNumber);
|
const filteredRows = this.filterRows(this.logList[tableType], omitQuery);
|
||||||
this.table.render(rows);
|
const rows = this.cutPagginationPage(filteredRows, query.pageNumber);
|
||||||
|
this.table.render(rows);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,12 +2,11 @@ import Component from '../component/index';
|
|||||||
import routeService from '../../services/RouteService';
|
import routeService from '../../services/RouteService';
|
||||||
import {createElement} from '../../utils/elementUtils';
|
import {createElement} from '../../utils/elementUtils';
|
||||||
import {prepareToNumber} from '../../utils/urlUtils';
|
import {prepareToNumber} from '../../utils/urlUtils';
|
||||||
|
import {EVENTS} from '../../consts';
|
||||||
|
|
||||||
const LEFT_ICON = '«';
|
const LEFT_ICON = '«';
|
||||||
const RIGHT_ICON = '»';
|
const RIGHT_ICON = '»';
|
||||||
|
|
||||||
const CHAGE_PAGE = 'changePage';
|
|
||||||
|
|
||||||
class Pagination extends Component {
|
class Pagination extends Component {
|
||||||
buttons = [];
|
buttons = [];
|
||||||
|
|
||||||
@ -19,9 +18,7 @@ class Pagination extends Component {
|
|||||||
|
|
||||||
this.container = this.mainNode.querySelector('.pagination');
|
this.container = this.mainNode.querySelector('.pagination');
|
||||||
|
|
||||||
routeService.onChange(() => {
|
this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, this.renderButtons);
|
||||||
this.renderButtons();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOneButton = (text, isDisabled = false) => {
|
renderOneButton = (text, isDisabled = false) => {
|
||||||
@ -99,12 +96,6 @@ class Pagination extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.renderOneButton(RIGHT_ICON, this.currentPage >= this.countPages);
|
this.renderOneButton(RIGHT_ICON, this.currentPage >= this.countPages);
|
||||||
|
|
||||||
this.next(CHAGE_PAGE, this.currentPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
onPageChange = (listener) => {
|
|
||||||
this.subscribe(CHAGE_PAGE, listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import routeService from '../../services/RouteService';
|
|||||||
import NotFoundPage from '../not-found-page';
|
import NotFoundPage from '../not-found-page';
|
||||||
|
|
||||||
import './RouterPagesContainer.css';
|
import './RouterPagesContainer.css';
|
||||||
|
import {EVENTS} from '../../consts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface Route
|
* @interface Route
|
||||||
@ -35,7 +36,7 @@ class RouterPagesContainer extends Component {
|
|||||||
constructor () {
|
constructor () {
|
||||||
super('#page-container', document.body);
|
super('#page-container', document.body);
|
||||||
|
|
||||||
routeService.onChange(({url}) => {
|
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, ({url}) => {
|
||||||
// Если под указанный url нет pageComponent, то будет испольщована страница NotFound
|
// Если под указанный url нет pageComponent, то будет испольщована страница NotFound
|
||||||
const {pageComponent: PageComponent = NotFoundPage} = this.routes.find((route) => {
|
const {pageComponent: PageComponent = NotFoundPage} = this.routes.find((route) => {
|
||||||
return route.url === url;
|
return route.url === url;
|
||||||
@ -52,7 +53,6 @@ class RouterPagesContainer extends Component {
|
|||||||
this.currentPage = new PageComponent('#page', this.mainNode);
|
this.currentPage = new PageComponent('#page', this.mainNode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -29,3 +29,8 @@ export const LOG_COLS = {
|
|||||||
[LOG_TYPE.SERVER]: SERVER_COLS,
|
[LOG_TYPE.SERVER]: SERVER_COLS,
|
||||||
[LOG_TYPE.CLIENT]: CLIENT_COLS,
|
[LOG_TYPE.CLIENT]: CLIENT_COLS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EVENTS = {
|
||||||
|
ROUTE_CHANGE: 'routeChange',
|
||||||
|
ROUTE_SEARCH_CHANGE: 'routeSearchChange'
|
||||||
|
};
|
||||||
|
|||||||
@ -8,13 +8,19 @@ class EmitService {
|
|||||||
*/
|
*/
|
||||||
_events;
|
_events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Подписки на события других Емитеров
|
||||||
|
*/
|
||||||
|
_subscribes;
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this._events = {};
|
this._events = {};
|
||||||
|
this._subscribes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Метод подписки на события компонента
|
* Метод подписки на события текущего Емитера
|
||||||
* @param {string} eventName - событие компонента, на которое будет реагировать обработчик
|
* @param {string} eventName - событие Емитера, на которое будет реагировать обработчик
|
||||||
* @param {Function} listener - обработчик события
|
* @param {Function} listener - обработчик события
|
||||||
* @example
|
* @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 - событие, которое необходимо сгенерировать
|
* @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 = () => {
|
clearSubscribes = () => {
|
||||||
|
this._subscribes.forEach(({emiter, eventName, listener}) => {
|
||||||
|
emiter.unsubscribe(eventName, listener);
|
||||||
|
});
|
||||||
|
this._subscribes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очищает все события и слушателей
|
||||||
|
*/
|
||||||
|
clearEvents = () => {
|
||||||
this._events = {};
|
this._events = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import EmitService from './EmitService';
|
import EmitService from './EmitService';
|
||||||
import {parse} from 'querystring';
|
import {parse} from 'querystring';
|
||||||
import {makeUrlWithQuery} from '../utils/urlUtils';
|
import {makeUrlWithQuery} from '../utils/urlUtils';
|
||||||
|
import {EVENTS} from '../consts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function RouterListener
|
* @function RouterListener
|
||||||
@ -8,12 +9,6 @@ import {makeUrlWithQuery} from '../utils/urlUtils';
|
|||||||
* @param {Object<string, string>} query - объект ключ-значение из url
|
* @param {Object<string, string>} query - объект ключ-значение из url
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Константа для события изменения роута
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
const ROUTE_CHANGE = 'routeChange';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Класс для работы с роутингом. Позволяет переходить по роутам и генерит событие изменения роута.
|
* Класс для работы с роутингом. Позволяет переходить по роутам и генерит событие изменения роута.
|
||||||
*/
|
*/
|
||||||
@ -28,7 +23,19 @@ class RouteService extends EmitService {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.history = window.history;
|
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
|
* @private
|
||||||
*/
|
*/
|
||||||
generateNext = () => {
|
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
|
* // Это создаст строку в url - site.ru/users?key=testApi&author=Petrov
|
||||||
*/
|
*/
|
||||||
goTo = (url, query = {}) => {
|
goTo = (url, query) => {
|
||||||
const prepareQuery = Object.entries(query)
|
this._nextUrl(url, query);
|
||||||
.reduce((memo, [key, value]) => {
|
|
||||||
if (value) {
|
|
||||||
memo[key] = value;
|
|
||||||
}
|
|
||||||
return memo;
|
|
||||||
}, {});
|
|
||||||
const urlWithQuery = makeUrlWithQuery(url, prepareQuery);
|
|
||||||
this.history.pushState({}, '', urlWithQuery);
|
|
||||||
this.generateNext();
|
this.generateNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,23 +97,11 @@ class RouteService extends EmitService {
|
|||||||
*/
|
*/
|
||||||
pushQuery = (newQuery, isClear = false) => {
|
pushQuery = (newQuery, isClear = false) => {
|
||||||
const {url, query} = this.getUrlData();
|
const {url, query} = this.getUrlData();
|
||||||
this.goTo(url, {
|
this._nextUrl(url, {
|
||||||
...(isClear ? {} : query),
|
...(isClear ? {} : query),
|
||||||
...newQuery,
|
...newQuery,
|
||||||
});
|
});
|
||||||
}
|
this.generatePushQuery();
|
||||||
|
|
||||||
/**
|
|
||||||
* С помощью этого метода подписываемся на событие изменения роута.
|
|
||||||
* @param {RouterListener} listener - слушатель для события изменения роута
|
|
||||||
* @example
|
|
||||||
* // Подписка на изменение url
|
|
||||||
* _.onChange(({url, query}) => {
|
|
||||||
* ...
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
onChange = (listener) => {
|
|
||||||
this.subscribe(ROUTE_CHANGE, listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user