Creating a Product Card
The next step is to add a product card and a link to it from the general list. To do this, we will add another app.html
call in our backend file. Thus, we are adding another route handler to this file.
app.html('/card/:id', async(ctx, req) => {
return <html>
{req.params.id}
</html>
})
Note that there is a parameter :id
in the address; this template covers all addresses of the form:
https://{account domain}/backend~card/{card id}
Now let's create a link to this card from the list. First, we will declare a constant to which we will assign the product card handler.
const cardScreen = app.html('/card/:id', async(ctx, req) => {
// further code
Next, we need to use this constant in the code that generates the list of cards to create a link from the list to the product card. We will also replace the <div>
tag with an <a>
tag.
{items.map(item =>
<a class="card" href={cardScreen({id: item.id}).url()}>
<img src={item.image?.getThumbnailUrl(500, 300)}/>
<div class='card-body p-2'>
<h3>{item.title}</h3>
<div>{item.material}</div>
<div>{item.dimensions}</div>
</div>
</a>
)}
Notice how the link address is formed; it passes the card's id:
cardScreen({id: item.id}).url()
Now let's create a back link from the card to the list page. For this, we will similarly assign the list page handler to a constant.
const mainScreen = app.html('/', async(ctx, req) => {
// further code
And we will create a link to this screen in the product card layout.
app.html('/card/:id', async(ctx, req) => {
return <html>
<h3 class="mb-3"><a href={mainScreen.url()}>Catalog of Paintings</a></h3>
{req.params.id}
</html>
})
Now let's try to open our code and see how the list and card work. The list displays the code, and you can click on any item to go to the card page, where we only see the id.
Let's add product data to the card page. For this, we need to "fetch" the painting data by its id. We will use the table method getById.
const cardScreen = app.html('/card/:id', async(ctx, req) => {
const item = await catalogTable.getById(ctx, req.params.id!)
return <html>
<h3 class="mb-3"><a href={mainScreen.url()}>Catalog of Paintings</a></h3>
<h1 class={"fs-1 fs-header fw-bold"}>{item.title}</h1>
</html>
})
We will present the complete layout of the card here in the final code below.
Final Code
import {jsx} from '@app/html-jsx'
import {Heap} from '@app/heap'
const catalogTable = Heap.Table('pictures', {
title: Heap.String(), // title. string
image: Heap.ImageFile(), // image. required
material: Heap.Optional(Heap.String()), // material. optional. string.
dimensions: Heap.Optional(Heap.String()), // dimensions. optional. string.
price: Heap.NonRequired(Heap.Number(), 0), // price. number. default 0
})
let currency = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
maximumFractionDigits: 0,
});
const mainScreen = app.html('/', async(ctx, req) => {
const items = await catalogTable.findAll(ctx)
return <html>
<head>
<title>Catalog of Paintings</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"/>
</head>
<body>
<div class="container p-3 col-md-6 col-12">
<h1 class={"fs-1 fs-header fs-bold"}>Catalog of Paintings</h1>
{items.map(item =>
<a class="card" href={cardScreen({id: item.id}).url()}>
<img src={item.image?.getThumbnailUrl(500, 300)}/>
<div class='card-body p-2'>
<h3>{item.title}</h3>
<div>{item.material}</div>
<div>{item.dimensions}</div>
</div>
</a>
)}
</div>
</body>
</html>
})
const cardScreen = app.html('/card/:id', async(ctx, req) => {
const item = await catalogTable.getById(ctx, req.params.id!)
return <html>
<h3 class="mb-3"><a href={mainScreen.url()}>Catalog of Paintings</a></h3>
<div class={"row g-10"}>
<div class={"col-md-8 col-12"}>
<img class="mw-100" src={item.image?.getThumbnailUrl(900)}/>
</div>
<div class={"col-md-4 col-12"}>
<h1 class={"fs-1 fs-header fw-bold"}>{item.title}</h1>
<div>{item.material}</div>
<div>{item.dimensions}</div>
{item.price && <div class={"fs-2"}>{currency.format(item.price)}</div>}
</div>
</div>
</html>
})