Commit 323a326c authored by Alfredo Sumaran's avatar Alfredo Sumaran

Improve pagination when searching or filtering

[ci skip]
parent ea531e1e
...@@ -8,7 +8,15 @@ export default class FilterableList { ...@@ -8,7 +8,15 @@ export default class FilterableList {
this.filterForm = form; this.filterForm = form;
this.listFilterElement = filter; this.listFilterElement = filter;
this.listHolderElement = holder; this.listHolderElement = holder;
this.filterUrl = `${this.filterForm.getAttribute('action')}?${$(this.filterForm).serialize()}`; this.isBusy = false;
}
getFilterEndpoint() {
return `${this.filterForm.getAttribute('action')}?${$(this.filterForm).serialize()}`;
}
getPagePath() {
return this.getFilterEndpoint();
} }
initSearch() { initSearch() {
...@@ -20,9 +28,19 @@ export default class FilterableList { ...@@ -20,9 +28,19 @@ export default class FilterableList {
} }
onFilterInput() { onFilterInput() {
const url = this.filterForm.getAttribute('action'); const $form = $(this.filterForm);
const data = $(this.filterForm).serialize(); const queryData = {};
this.filterResults(url, data, 'filter-input'); const filterGroupsParam = $form.find('[name="filter_groups"]').val();
if (filterGroupsParam) {
queryData.filter_groups = filterGroupsParam;
}
this.filterResults(queryData);
if (this.setDefaultFilterOption) {
this.setDefaultFilterOption();
}
} }
bindEvents() { bindEvents() {
...@@ -33,42 +51,44 @@ export default class FilterableList { ...@@ -33,42 +51,44 @@ export default class FilterableList {
this.listFilterElement.removeEventListener('input', this.debounceFilter); this.listFilterElement.removeEventListener('input', this.debounceFilter);
} }
filterResults(url, data, comingFrom) { filterResults(queryData) {
const endpoint = url || this.filterForm.getAttribute('action'); if (this.isBusy) {
const additionalData = data || $(this.filterForm).serialize(); return false;
}
$(this.listHolderElement).fadeTo(250, 0.5); $(this.listHolderElement).fadeTo(250, 0.5);
return $.ajax({ return $.ajax({
url: endpoint, url: this.getFilterEndpoint(),
data: additionalData, data: queryData,
type: 'GET', type: 'GET',
dataType: 'json', dataType: 'json',
context: this, context: this,
complete: this.onFilterComplete, complete: this.onFilterComplete,
beforeSend: () => {
this.isBusy = true;
},
success: (response, textStatus, xhr) => { success: (response, textStatus, xhr) => {
if (this.preOnFilterSuccess) { this.onFilterSuccess(response, xhr, queryData);
this.preOnFilterSuccess(comingFrom);
}
this.onFilterSuccess(response, xhr);
}, },
}); });
} }
onFilterSuccess(data) { onFilterSuccess(response, xhr, queryData) {
if (data.html) { if (response.html) {
this.listHolderElement.innerHTML = data.html; this.listHolderElement.innerHTML = response.html;
} }
// Change url so if user reload a page - search results are saved // Change url so if user reload a page - search results are saved
return window.history.replaceState({ const currentPath = this.getPagePath(queryData);
page: this.filterUrl,
}, document.title, this.filterUrl); return window.history.replaceState({
page: currentPath,
}, document.title, currentPath);
} }
onFilterComplete() { onFilterComplete() {
this.isBusy = false;
$(this.listHolderElement).fadeTo(250, 1); $(this.listHolderElement).fadeTo(250, 1);
} }
} }
<script> <script>
import TablePaginationComponent from '~/vue_shared/components/table_pagination'; import TablePaginationComponent from '~/vue_shared/components/table_pagination.vue';
import eventHub from '../event_hub';
export default { export default {
components: { components: {
...@@ -16,11 +17,10 @@ export default { ...@@ -16,11 +17,10 @@ export default {
}, },
}, },
methods: { methods: {
change(pageNumber) { change(page) {
const param = gl.utils.setParamInURL('page', pageNumber); const filterGroupsParam = gl.utils.getParameterByName('filter_groups');
const sortParam = gl.utils.getParameterByName('sort');
gl.utils.visitUrl(param); eventHub.$emit('fetchPage', page, filterGroupsParam, sortParam);
return param;
}, },
}, },
}; };
......
...@@ -2,12 +2,24 @@ import FilterableList from '~/filterable_list'; ...@@ -2,12 +2,24 @@ import FilterableList from '~/filterable_list';
import eventHub from './event_hub'; import eventHub from './event_hub';
export default class GroupFilterableList extends FilterableList { export default class GroupFilterableList extends FilterableList {
constructor(form, filter, holder) { constructor({ form, filter, holder, filterEndpoint, pagePath }) {
super(form, filter, holder); super(form, filter, holder);
this.form = form;
this.filterEndpoint = filterEndpoint;
this.pagePath = pagePath;
this.$dropdown = $('.js-group-filter-dropdown-wrap'); this.$dropdown = $('.js-group-filter-dropdown-wrap');
} }
getFilterEndpoint() {
return this.filterEndpoint;
}
getPagePath(queryData) {
const params = queryData ? $.param(queryData) : '';
const queryString = params ? `?${params}` : '';
return `${this.pagePath}${queryString}`;
}
bindEvents() { bindEvents() {
super.bindEvents(); super.bindEvents();
...@@ -21,26 +33,45 @@ export default class GroupFilterableList extends FilterableList { ...@@ -21,26 +33,45 @@ export default class GroupFilterableList extends FilterableList {
onFormSubmit(e) { onFormSubmit(e) {
e.preventDefault(); e.preventDefault();
this.filterResults(); const $form = $(this.form);
const filterGroupsParam = $form.find('[name="filter_groups"]').val();
const queryData = {};
if (filterGroupsParam) {
queryData.filter_groups = filterGroupsParam;
}
this.filterResults(queryData);
this.setDefaultFilterOption();
}
setDefaultFilterOption() {
const defaultOption = $.trim(this.$dropdown.find('.dropdown-menu a:first-child').text());
this.$dropdown.find('.dropdown-label').text(defaultOption);
} }
onOptionClick(e) { onOptionClick(e) {
e.preventDefault(); e.preventDefault();
const currentOption = $.trim(e.currentTarget.text);
this.filterUrl = e.currentTarget.href; const queryData = {};
this.$dropdown.find('.dropdown-label').text(currentOption); const sortParam = gl.utils.getParameterByName('sort', e.currentTarget.href);
this.filterResults(this.filterUrl);
}
preOnFilterSuccess(comingFrom) { if (sortParam) {
if (comingFrom === 'filter-input') { queryData.sort = sortParam;
this.filterUrl = `${this.filterForm.getAttribute('action')}?${$(this.filterForm).serialize()}`;
} }
this.filterResults(queryData);
// Active selected option
this.$dropdown.find('.dropdown-label').text($.trim(e.currentTarget.text));
// Clear current value on search form
this.form.querySelector('[name="filter_groups"]').value = '';
} }
onFilterSuccess(data, xhr) { onFilterSuccess(data, xhr, queryData) {
super.onFilterSuccess(data); super.onFilterSuccess(data, xhr, queryData);
const paginationData = { const paginationData = {
'X-Per-Page': xhr.getResponseHeader('X-Per-Page'), 'X-Per-Page': xhr.getResponseHeader('X-Per-Page'),
'X-Page': xhr.getResponseHeader('X-Page'), 'X-Page': xhr.getResponseHeader('X-Page'),
......
...@@ -37,7 +37,9 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -37,7 +37,9 @@ document.addEventListener('DOMContentLoaded', () => {
let parentId = null; let parentId = null;
let getGroups = null; let getGroups = null;
let page = null; let page = null;
let sort = null;
let pageParam = null; let pageParam = null;
let sortParam = null;
let filterGroups = null; let filterGroups = null;
let filterGroupsParam = null; let filterGroupsParam = null;
...@@ -55,9 +57,14 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -55,9 +57,14 @@ document.addEventListener('DOMContentLoaded', () => {
filterGroups = filterGroupsParam; filterGroups = filterGroupsParam;
} }
getGroups = this.service.getGroups(parentId, page, filterGroups); sortParam = gl.utils.getParameterByName('sort');
if (sortParam) {
sort = sortParam;
}
getGroups = this.service.getGroups(parentId, page, filterGroups, sort);
getGroups.then((response) => { getGroups.then((response) => {
eventHub.$emit('updateGroups', response.json(), parentGroup); this.updateGroups(response.json(), parentGroup);
}) })
.catch(() => { .catch(() => {
// TODO: Handle error // TODO: Handle error
...@@ -65,6 +72,17 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -65,6 +72,17 @@ document.addEventListener('DOMContentLoaded', () => {
return getGroups; return getGroups;
}, },
fetchPage(page, filterGroups, sort) {
this.service.getGroups(null, page, filterGroups, sort)
.then((response) => {
this.updateGroups(response.json());
this.updatePagination(response.headers);
$.scrollTo(0);
})
.catch(() => {
// TODO: Handle error
});
},
toggleSubGroups(parentGroup = null) { toggleSubGroups(parentGroup = null) {
if (!parentGroup.isOpen) { if (!parentGroup.isOpen) {
this.store.resetGroups(parentGroup); this.store.resetGroups(parentGroup);
...@@ -95,9 +113,18 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -95,9 +113,18 @@ document.addEventListener('DOMContentLoaded', () => {
const filter = document.querySelector('.js-groups-list-filter'); const filter = document.querySelector('.js-groups-list-filter');
const holder = document.querySelector('.js-groups-list-holder'); const holder = document.querySelector('.js-groups-list-holder');
groupFilterList = new GroupFilterableList(form, filter, holder); const opts = {
form,
filter,
holder,
filterEndpoint: el.dataset.endpoint,
pagePath: el.dataset.path,
};
groupFilterList = new GroupFilterableList(opts);
groupFilterList.initSearch(); groupFilterList.initSearch();
eventHub.$on('fetchPage', this.fetchPage);
eventHub.$on('toggleSubGroups', this.toggleSubGroups); eventHub.$on('toggleSubGroups', this.toggleSubGroups);
eventHub.$on('leaveGroup', this.leaveGroup); eventHub.$on('leaveGroup', this.leaveGroup);
eventHub.$on('updateGroups', this.updateGroups); eventHub.$on('updateGroups', this.updateGroups);
...@@ -106,7 +133,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -106,7 +133,7 @@ document.addEventListener('DOMContentLoaded', () => {
mounted() { mounted() {
this.fetchGroups() this.fetchGroups()
.then((response) => { .then((response) => {
eventHub.$emit('updatePagination', response.headers); this.updatePagination(response.headers);
}) })
.catch(() => { .catch(() => {
// TODO: Handle error // TODO: Handle error
......
...@@ -8,7 +8,7 @@ export default class GroupsService { ...@@ -8,7 +8,7 @@ export default class GroupsService {
this.groups = Vue.resource(endpoint); this.groups = Vue.resource(endpoint);
} }
getGroups(parentId, page, filterGroups) { getGroups(parentId, page, filterGroups, sort) {
const data = {}; const data = {};
if (parentId) { if (parentId) {
...@@ -22,6 +22,10 @@ export default class GroupsService { ...@@ -22,6 +22,10 @@ export default class GroupsService {
if (filterGroups) { if (filterGroups) {
data.filter_groups = filterGroups; data.filter_groups = filterGroups;
} }
if (sort) {
data.sort = sort;
}
} }
return this.groups.get(data); return this.groups.get(data);
......
...@@ -167,8 +167,8 @@ ...@@ -167,8 +167,8 @@
if the name does not exist this function will return `null` if the name does not exist this function will return `null`
otherwise it will return the value of the param key provided otherwise it will return the value of the param key provided
*/ */
w.gl.utils.getParameterByName = (name) => { w.gl.utils.getParameterByName = (name, parseUrl) => {
const url = window.location.href; const url = parseUrl || window.location.href;
name = name.replace(/[[\]]/g, '\\$&'); name = name.replace(/[[\]]/g, '\\$&');
const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`);
const results = regex.exec(url); const results = regex.exec(url);
......
.js-groups-list-holder .js-groups-list-holder
#dashboard-group-app{ data: { endpoint: dashboard_groups_path(format: :json) } } #dashboard-group-app{ data: { endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path } }
%groups-component{ ':groups' => 'state.groups', ':page-info' => 'state.pageInfo' } %groups-component{ ':groups' => 'state.groups', ':page-info' => 'state.pageInfo' }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment