top of page

Forum Posts

Alexander Zaytsev
Velo Team
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
Query selector for child elements content media
5
1
525
Alexander Zaytsev
Velo Team
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
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
A tiny event-based state manager Storeon for Velo content media
7
5
179
Alexander Zaytsev
Velo Team
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

Velo Team
+4
More actions
bottom of page