Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 88 additions & 43 deletions Vue InstantSearch/routing-full-url/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ import algoliasearch from 'algoliasearch/lite';
import { history as historyRouter } from 'instantsearch.js/es/lib/routers';
import 'instantsearch.css/themes/algolia-min.css';

// Returns a slug from the category name.
// Spaces are replaced by "+" to make
// the URL easier to read and other
// characters are encoded.
function getCategorySlug(name) {
return name
.split(' ')
.map(encodeURIComponent)
.join('+');
}

// Returns a name from the category slug.
// The "+" are replaced by spaces and other
// characters are decoded.
function getCategoryName(slug) {
return slug
.split('+')
.map(decodeURIComponent)
.join(' ');
}

export default {
data() {
return {
Expand All @@ -52,65 +73,89 @@ export default {
),
routing: {
router: historyRouter({
windowTitle(routeState) {
return `Website / Find ${routeState.q} in ${
routeState.brands
} brands`;
windowTitle({ category, query }) {
const queryTitle = query ? `Results for "${query}"` : 'Search';

if (category) {
return `${category} – ${queryTitle}`;
}

return queryTitle;
},
createURL({ routeState, location }) {
let baseUrl = location.href.split('/search/')[0];
if (
!routeState.q &&
routeState.brands === 'all' &&
routeState.p === 1
)
return baseUrl;
if (baseUrl[baseUrl.length - 1] !== '/') baseUrl += '/';
let routeStateArray = [
'q',
encodeURIComponent(routeState.q),
'brands',
encodeURIComponent(routeState.brands),
'p',
routeState.p,
];

return `${baseUrl}search/${routeStateArray.join('/')}`;

createURL({ qsModule, routeState, location }) {
const urlParts = location.href.match(/^(.*?)\/search/);
const baseUrl = `${urlParts ? urlParts[1] : ''}/`;

const categoryPath = routeState.category
? `${getCategorySlug(routeState.category)}/`
: '';
const queryParameters = {};

if (routeState.query) {
queryParameters.query = encodeURIComponent(routeState.query);
}
if (routeState.page !== 1) {
queryParameters.page = routeState.page;
}
if (routeState.brands) {
queryParameters.brands = routeState.brands.map(
encodeURIComponent
);
}

const queryString = qsModule.stringify(queryParameters, {
addQueryPrefix: true,
arrayFormat: 'repeat',
});

return `${baseUrl}search/${categoryPath}${queryString}`;
},
parseURL({ location }) {
let routeStateString = location.href.split('/search/')[1];
if (routeStateString === undefined) return {};
const routeStateValues = routeStateString.match(
/^q\/(.*?)\/brands\/(.*?)\/p\/(.*?)$/

parseURL({ qsModule, location }) {
const pathnameMatches = location.pathname.match(
/search\/(.*?)\/?$/
);
const category = getCategoryName(
(pathnameMatches && pathnameMatches[1]) || ''
);
const { query = '', page, brands = [] } = qsModule.parse(
location.search.slice(1)
);
// `qs` does not return an array when there's a single value.
const allBrands = Array.isArray(brands)
? brands
: [brands].filter(Boolean);

return {
q: decodeURIComponent(routeStateValues[1]),
brands: decodeURIComponent(routeStateValues[2]),
p: routeStateValues[3],
query: decodeURIComponent(query),
page,
brands: allBrands.map(decodeURIComponent),
category,
};
},
}),

stateMapping: {
stateToRoute(uiState) {
return {
q: uiState.query || '',
brands:
(uiState.refinementList &&
uiState.refinementList.brand &&
uiState.refinementList.brand.join('~')) ||
'all',
p: uiState.page || 1,
query: uiState.query,
page: uiState.page,
brands: uiState.refinementList && uiState.refinementList.brand,
category: uiState.menu && uiState.menu.categories,
};
},
routeToState(routeState) {
if (routeState.brands === 'all') routeState.brands = undefined;

routeToState(routeState) {
return {
query: routeState.q,
query: routeState.query,
page: routeState.page,
menu: {
categories: routeState.category,
},
refinementList: {
brand: routeState.brands && routeState.brands.split('~'),
brand: routeState.brands,
},
page: routeState.p,
};
},
},
Expand Down
23 changes: 2 additions & 21 deletions Vue InstantSearch/routing-state-mapping/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<script>
import algoliasearch from 'algoliasearch/lite';
import { history as historyRouter } from 'instantsearch.js/es/lib/routers';
import { simple as simpleMapping } from 'instantsearch.js/es/lib/stateMappings';
import 'instantsearch.css/themes/algolia-min.css';

export default {
Expand All @@ -52,27 +53,7 @@ export default {
),
routing: {
router: historyRouter(),
stateMapping: {
stateToRoute(uiState) {
return {
query: uiState.query,
brands:
uiState.refinementList &&
uiState.refinementList.brand &&
uiState.refinementList.brand.join('~'),
page: uiState.page,
};
},
routeToState(routeState) {
return {
query: routeState.query,
refinementList: {
brand: routeState.brands && routeState.brands.split('~'),
},
page: routeState.page,
};
},
},
stateMapping: simpleMapping(),
},
};
},
Expand Down