Single product
The single product page template
is designated to view product details such as the title, description, type, color, size, price, and quantity. Each product will have its own details displayed on this page.
The main structure of this product page template is showing the product primary components, and then showing the related essential components, options components, and related offers components.
Following is the page location and url:
└── src
├── views
├── pages
| ├── product
| ...
| ├── single.twig
| ...
...
Example
Variables
Components
This page starts by displaying the breadcrumbs
component. The {% component breadcrumbs %}
line returns the current navigation for the user:
{% component 'header.breadcrumbs'%}
In addition, the developer may show the product.offer
component:
{% component 'product.offer' %}
Also, the customers' comments on a specific product may be displayed using the component comments
:
{% component 'comments' %}
Similary, this page may show similar products to the current product using the component product.similar-products
. This will make it more and more reachable for the customers:
{% component 'product.similar-products' %}
JS Web Components
Single Product page may include the following JS Web Components, which are ready-made designs and style-sets of web components for Salla stores.
Add Product
<salla-add-product>
Button
<salla-button>
Gifting
<salla-gifting>
Installment
<salla-installment>
Products Slider
<salla-products-slider>
Quick Order
<salla-quick-order>
Quantity Input
<salla-quantity-input>
Rating Stars
<salla-rating-stars>
Slider
<salla-slider>
Social Share
<salla-social-share>
Hooks
In the single product template page, the developer displays the product details such as the title, description, color, size, price, and quantity. Each product will have its own details displayed on this page.
In some cases, the product may include "extra" services related, for example, to the payment services. Using hook {% hook 'product:single.description.start' %}
in the single product code shows these extra services, as we see in the following example:
{% hook 'product:single.description.start' %}
{% hook 'product:single.description.end' %}
In addition, the single product page template
may call the following hooks in order to inject extra information:
{% hook 'product:single.description' %}
{% hook 'product:single.form.start' %}
{% hook 'product:single.form.end' %}
Usage
This page template starts with extracting main variables from the product
object, such as product title and id. Using this. object, the developer has the ability to perform many tasks related to the product page, as we will see in the following parts.
Product description
Initially, several variables, such as product.promotion_title
, product.brand
, product.name
, product.rating
, product.subtitle
, product.description
, and product.tags
, can be used by the developer to display multiple pieces of information about a single product.
{% if(product.promotion_title) %}
{{ product.promotion_title }}
{% endif %}
{% if product.brand %}
<img title="{{ product.brand.name }}" src="{{ product.brand.logo }}" alt="{{ product.brand.name }}"/>
{% endif %}
<h1>{{ product.name }}</h1>
{% if product.rating %}
{{ product.rating }}
{% endif %}
{% if product.subtitle %}
<h3>{{ product.subtitle }}</h3>
{% endif %}
{% if product.has_read_more %}
<p>{{ product.description|raw }}</p>
{# Read More Button #}
<a> {{ trans('pages.products.read_more') }} </a>
{% else %}
{{ product.description|raw }}
{% endif %}
{% if product.tags|length %}
{% for tag in product.tags %}
<a href="{{ tag.url }}"> {{ tag.name }} </a>
{% endfor %}
{% endif %}
Product images
The variable product.images
contains the product's several images, which can be displayed using a for-loop statement.
{% for image in product.images %}
<img src="{{ image.url }}" alt="{{ image.alt }}" />
{% endfor %}
Add product to the cart form (Options and Quantity)
The important part of this page is the form for adding the product to the cart. This <form>
basically consists of two main sections. The first section is to show the product's options, and the product price and quantity.
The product.options
uses a for-loop
statement in order to show each available option for the product. Both hooks {% hook 'product:single.form.start' %}
and {% hook 'product:single.form.end' %}
are enclosing this part.
The second section is to show the product price and availability. If the product is on sale, the variable product.is_on_sale
returns the boolean value true. The developer can use the if
statement to check that and then display product.price
, product.sale_price
, product.regular_price
, or any other value as needed.
The variables product.sold_quantity
and product.can_show_remained_quantity
reflect the available quantity of the product. The developer may display the product's price along with its available quantity after checking if product.is_available
is true. It is worth mentioning here that the variable product.notify_availability
can be checked in order to give the user the option of being notified about receiving a new quantity of that product.
<form enctype="multipart/form-data" method="post"
onchange="salla.product.getPrice(new FormData(event.currentTarget))"
onsubmit="return salla.form.submit('cart.addItem');">
<input name="id" type="hidden" value="{{ product.id }}"/>
{% hook 'product:single.form.start' %}
{% if product.options|length %}
{% for option_index, option in product.options %}
{% if option.type in ["splitter"] %}
{{ option.element|raw }}
{% else %}
{% if option.type != "donation" %}
<label for="options[{{ option.id }}]">
<strong>
{{ option.name }}
{% if option.required %}<span>*</span>{% endif %}
</strong>
<small>{{ option.placeholder }}</small>
</label>
{% endif %}
{{ option.element(product.id)|raw }}
{% endif %}
{% endfor %}
{% endif %}
{% if product.can_add_note or product.can_upload_file %}
<strong>{{ trans('pages.products.attachments') }}</strong>
{% if product.can_add_note %}
<button type="button" data-show="note_{{ product.id }}">
{{ trans('pages.products.add_note') }}
</button>
{% endif %}
{% if product.can_upload_file %}
<button type="button" data-show="file_{{ product.id }}"> {{ trans('pages.products.add_file') }} </button>
{% endif %}
{% if product.can_add_note %}
<textarea cols="30" name="notes" rows="10"
placeholder="{{ trans('pages.products.notes_placeholder') }}">
{{ product.notes }}</textarea>
{% endif %}
{% if product.can_upload_file %}
<input name="{{ is_cart?'image_file':'file' }}" type="file"
data-url="{{ link('cart/image') }}" data-instant-upload=""
data-files="{{ product.attachments }}" data-item-id="{{ product.id }}"
data-max-file-size="3MB"/>
{% endif %}
{% endif %}
{% hook 'product:single.form.end' %}
{# Quantity #}
{% if product.is_hidden_quantity %}
<input name="quantity" type="hidden" value="1"/>
{% else %}
{{ trans('pages.products.quantity') }}
<salla-quantity-input max="{{ product.max_quantity }}" value="1" name="quantity">
</salla-quantity-input>
{% endif %}
{# Price #}
{{ trans('pages.products.price') }}
{% if product.is_on_sale %}
<h4>{{ product.sale_price|money }}</h4>
{{ product.regular_price|money }}
{% else %}
<h4>{{ product.price|money }}</h4>
{% endif %}
<salla-add-product-button product-id="{{ product.id }}"
product-status="{{ product.status }}"
product-type="{{ product.type }}">
</salla-add-product-button>
</form>
Product Meta Data
Merchants are able to introduce custom fields for the product, and you can render such fields by using the <salla-metadata>
JS Web Component in the following manner:
{% if product.has_metadata %} <salla-metadata></salla-metadata> {% endif %}
:::tip[Educational Clip]
:::