top of page
Forum Posts
Alexander Zaytsev
Velo Team
Jan 13, 2022
In Tips, Tutorials, Examples
Get the child elements inside a parent node. Let's suppose we have a few containers with checkboxes in each of them. We don't know how many checkboxes will be in each container at the final design. Each container has a "select all" named checkbox that allows us to check on/off all checkboxes into a current target container. We want to provide a good abstract solution for avoiding hard coding of elements' ID. And ensure scaling for any count of the elements in the future. And here, my solution on how to implement a child query selector for Velo. For Example: We have two groups with checkboxes: And when we click by a checkbox "All" we need to switch all checkboxes in target group. For example like this: $w.onReady(() => {
$w('#checkboxAllYellow').onChange((event) => {
findIn('#boxYellow').all('Checkbox').checked = event.target.checked;
});
$w('#checkboxAllBlue').onChange((event) => {
findIn('#boxBlue').all('Checkbox').checked = event.target.checked;
});
}); The next snippet of code it's a realization of this selector. We can find all Checkbox in # boxBlue and We can find all Checkbox in # boxYellow findIn("#ID").all("Type") /**
* @param {$w.Node} element
* @param {string} parentId
* @returns {boolean}
*/
const hasParent = (element, parentId) => {
while (element) {
element = element.parent;
if (element?.id === parentId) {
return true;
}
}
return false;
};
/**
* @param {WixElementSelector} selector
*/
export const findIn = (selector) => {
const parentId = selector.replace(/^#/, '');
return {
/**
* @template {keyof TypeNameToSdkType} T
* @param {T} type
* @returns {TypeNameToSdkType[T]}
*/
all(type) {
/** @type {any} */
const elements = $w(type);
const ids = elements.reduce((acc, element) => {
if (hasParent(element, parentId)) {
acc.push(`#${element.id}`);
}
return acc;
}, []);
return $w(ids.join(','));
},
};
}; I described in detail how it works in my blog post. Welcome to reading: - Velo by Wix: Query selector for child elements
5
1
525
Alexander Zaytsev
Velo Team
Aug 03, 2021
In Tips, Tutorials, Examples
The Wix Blog has built-in pagination with links. Links are required for good SEO. I have reproduced this pagination element that uses unique URLs and creates my own implementation. (Lots of code 😳) For that, I use a wix-router on the server part and I generating custom pagination by Repeater on the front-end part. So pagination is a difficult component. I share my solution I hope it will be helpful (interesting) to take a look at how it works.
DEMO: https://alexanderz5.wixsite.com/pagination/custom-blog
backend/routers.js
Route logic for pagination import wixData from 'wix-data';
import { ok, redirect, WixRouterSitemapEntry } from 'wix-router';
import urlJoin from 'url-join';
const hasContent = (val) => typeof val === 'string' && val.trim() !== '';
const isNumeric = (val) => hasContent(val) && /^[\d]+$/.test(val);
const parseNumber = (val) => ~~Math.abs(+val);
const getCategories = () => {
return wixData
.query('Blog/Categories')
.find()
.then((result) => result.items);
};
const getCategory = (label) => {
return wixData
.query('Blog/Categories')
.eq('label', label)
.limit(1)
.find()
.then((reslut) => reslut.items[0]);
};
const getPosts = async (pageSize, skipPages, categoryId = null) => {
let dataQuery = wixData.query('Blog/Posts');
if (hasContent(categoryId)) {
dataQuery = dataQuery.hasAll('categories', categoryId);
}
return dataQuery
.skip(skipPages)
.limit(pageSize)
.find();
};
const getParams = async (path) => {
const length = path.length;
const [one, two] = path.map((i) => i.toLowerCase());
if (length === 1) {
if (one === '') {
return {
page: 0,
label: '',
};
}
if (isNumeric(one)) {
return {
page: parseNumber(one),
label: '',
};
}
const category = await getCategory(one);
if (typeof category !== 'undefined') {
return {
page: 0,
categoryId: category._id,
label: category.label,
};
}
}
if (length === 2 && isNumeric(two)) {
const category = await getCategory(one);
if (typeof category !== 'undefined') {
return {
page: parseNumber(two),
categoryId: category._id,
label: category.label,
};
}
}
return { hasError: true };
};
/**
* @param {wix_router.WixRouterRequest} request
*/
export async function custom_blog_Router({ path, baseUrl, prefix }) {
const params = await getParams(path);
if (params.hasError) {
return redirect(urlJoin(baseUrl, prefix), '301');
}
const pageSize = 2;
const skip = (params.page === 0 ? 0 : params.page - 1) * pageSize;
const postsData = await getPosts(
pageSize,
skip,
params.categoryId,
);
return ok('custom-blog-page', {
pageSize,
posts: postsData.items,
currentPage: postsData.currentPage,
totalCount: postsData.totalCount,
totalPages: postsData.totalPages,
label: params.label,
});
}
/**
* @param {wix_router.WixRouterSitemapRequest} sitemapRequest
* @returns {Promise<wix_router.WixRouterSitemapEntry[]>}
*/
export async function custom_blog_SiteMap(sitemapRequest) {
const categories = await getCategories();
return categories.map((i) => {
const entry = new WixRouterSitemapEntry(i.label);
return Object.assign(entry, {
title: i.label,
pageName: i.label,
url: urlJoin('/', sitemapRequest.prefix, i.label),
});
});
} Custom Blog Page (code): Route page with repeater and custom pagination. import { getRouterData } from 'wix-window';
import { prefix } from 'wix-location';
import urlJoin from 'url-join';
import { paginate } from 'public/paginate';
$w.onReady(function () {
const {
posts,
currentPage,
totalCount,
pageSize,
label,
} = getRouterData();
const { data } = paginate({
totalCount,
currentPage,
maxPages: 4,
pageSize,
});
$w('#repeaterPosts').data = posts;
$w('#repeaterPosts').forEachItem(($item, itemData) => {
$item('#textTitle').text = itemData.title;
});
$w('#repeaterPagination').data = data;
$w('#repeaterPagination').forEachItem(($item, itemData) => {
$item('#button1').label = itemData.label;
if (itemData.isActive) {
$item('#button1').link = urlJoin('/', prefix, label, String(itemData.number));
} else {
$item('#button1').disable();
}
});
$w('#buttonLinkAll').link = urlJoin('/', prefix);
$w('#buttonLinkCss').link = urlJoin('/', prefix, 'css');
$w('#buttonLinkHtml').link = urlJoin('/', prefix, 'html');
$w('#buttonLinkJs').link = urlJoin('/', prefix, 'js');
}); public/paginate.js Here is a logic for creating repeater items for custom pagination export const paginate = ({
totalCount,
currentPage,
pageSize,
maxPages,
}) => {
const totalPages = Math.ceil(totalCount / pageSize);
let startPage = 1;
let endPage = totalPages;
if (currentPage > totalPages) {
currentPage = totalPages;
}
if (totalPages > maxPages) {
const maxPagesBeforeCurrentPage = Math.floor(maxPages / 2);
const maxPagesAfterCurrentPage = Math.ceil(maxPages / 2);
if (currentPage <= maxPagesBeforeCurrentPage) {
endPage = maxPages;
} else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
startPage = totalPages - maxPages + 1;
} else {
startPage = currentPage - maxPagesBeforeCurrentPage;
endPage = currentPage + maxPagesAfterCurrentPage;
}
}
const length = (endPage + 1) - startPage;
const data = Array.from({ length },
(_, index) => {
const number = startPage + index;
const id = String(number);
return {
_id: id,
label: id,
number,
isActive: number !== (currentPage + 1),
};
},
);
data.unshift({
_id: 'first',
label: '<<',
number: 1,
isActive: currentPage > 0,
}, {
_id: 'prev',
label: '<',
number: currentPage,
isActive: currentPage > 0,
});
data.push({
_id: 'next',
label: '>',
number: currentPage + 2,
isActive: (currentPage + 1) < totalPages,
}, {
_id: 'last',
label: '>>',
number: totalPages,
isActive: (currentPage + 1) < totalPages,
});
return {
data,
};
};
8
0
399
Alexander Zaytsev
Velo Team
Dec 22, 2019
In Coding with Velo
Update: Jan 10, 2021. Package is renamed to storeon-velo from corvid-storeon In the article “State management in Velo” Shahar Talmi bringing up a question about control app states in Velo. The state of app it’s a really big problem. If you have a lot of components dependencies between each other or a lot of user interaction, eventually, add a new feature or support app is going through suffering. In this article, I share my solution. It’s a very tiny library Storeon (core 180 bytes in gzip) with an easy interface. So I wrote a wrapper for integration with Velo. As a result, we have the state manager storeon-velo less than 90 lines of code. Post on Blog Live Demo
7
5
179
Alexander Zaytsev
Velo Team
Oct 29, 2019
In Coding with Velo
Hey gang!
I wrote an article about how we imitating the hover events on repeater container. You can read this on my blog post or medium.com. I hope it will be helpful.
3
1
73
Alexander Zaytsev
More actions
bottom of page