I want to make the "recently viewed products" widget a Slideshow, using slick Slider.
Here's what I did so far, although this might not be the correct approach:
I copied template and viewModel into my theme:
Magento_Catalog/web/template/product/list/listing.html
magento_Catalog/web/js/product/list/listing.js
The idea is basically to use 'afterRender' to run my custom function making the widget a slider.
This is working, the code is running and successfully making the widget a slider.
The problem ist, that every item in the product-list is becoming a new row on the slideshow instead of being added to the existing one.
HTML Result:
<ol class="product-items product-slider recently-viewed slick-initialized slick-slider" data-bind="afterRender: function (t) {addSlider(t);}">
<div class="slick-list draggable" data-repeat-index="0">
<div class="slick-track">
<li class="product-item slick-slide slick-current slick-active" data-slick-index="0" tabindex="0">
<div class="product-item-info">Product Item HTML Code here]</div>
</li>
</div>
</div>
<div class="slick-list draggable" data-repeat-index="1">
<div class="slick-track">
<li class="product-item slick-slide slick-current slick-active" data-slick-index="0" tabindex="0">
<div class="product-item-info">[Product Item HTML Code here]</div>
</li>
</div>
</div>
</ol>
expected output (taken from another product slider that's not a uiCOmponent) would be:
<ol class="products list items product-items product-slider slick-initialized slick-slider">
<div class="slick-list draggable">
<div class="slick-track">
<li class="item product product-item slick-slide slick-current slick-active">Child HTML here</li>
<li class="item product product-item slick-slide slick-active">Child HTML here</li>
<li class="item product product-item slick-slide slick-active">Child HTML here</li>
</div>
</div>
</ol>
So only one .slick-list
, containing one slick-track
, containing all the items.
My files look like this:
listing.html:
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<div if="hasData()"
class="block" css="additionalClasses">
<div class="block-title">
<strong role="heading"
aria-level="2"
text="label"/>
</div>
<div class="block-content">
<div css="'products-' + displayMode">
<ol class="product-items product-slider recently-viewed" afterRender="function (t) {addSlider(t);}">
<li class="product-item" repeat="foreach: filteredRows, item: '$row'">
<div class="product-item-info">
<fastForEach args="data: getRegion('general-area'), as: '$col'" >
<render args="$col.getBody()"/>
</fastForEach>
<div class="product-item-details">
<fastForEach args="data: getRegion('details-area'), as: '$col'" >
<render args="$col.getBody()"/>
</fastForEach>
<div if="regionHasElements('action-primary-area') || regionHasElements('action-secondary-area')"
class="product-item-actions">
<div class="actions-primary" if="regionHasElements('action-primary-area')">
<fastForEach args="data: getRegion('action-primary-area'), as: '$col'" >
<render args="$col.getBody()"/>
</fastForEach>
</div>
<div if="regionHasElements('action-secondary-area')"
class="actions-secondary"
data-role="add-to-links">
<fastForEach args="data: getRegion('action-secondary-area'), as: '$col'" >
<render args="$col.getBody()"/>
</fastForEach>
</div>
</div>
<div if="regionHasElements('description-area')"
class="product-item-description">
<fastForEach args="data: getRegion('description-area'), as: '$col'" >
<render args="$col.getBody()"/>
</fastForEach>
</div>
</div>
</div>
</li>
</ol>
</div>
</div>
</div>
I just added afterRender
to the ol.product-items
listing.js
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'ko',
'underscore',
'Magento_Ui/js/grid/listing',
//I added those two:
'jquery',
'slick'
], function (ko, _, Listing, $) {
'use strict';
return Listing.extend({
defaults: {
additionalClasses: '',
filteredRows: {},
limit: 5,
listens: {
elems: 'filterRowsFromCache',
'${ $.provider }:data.items': 'filterRowsFromServer'
}
},
//Magento functions here
//Added this function
/**
*
* @param target
*/
addSlider: function (target) {
$(target).slick({
dots: false,
infinite: false,
speed: 300,
slidesToShow: 5,
slidesToScroll: 1,
//variableWidth: true,
responsive: [
{
breakpoint: 1200,
settings: {
slidesToShow: 4
}
},
{
breakpoint: 900,
settings: {
slidesToShow: 3
}
},
{
breakpoint: 580,
settings: {
slidesToShow: 2,
arrows: false
}
},
{
breakpoint: 380,
settings: {
slidesToShow: 1,
arrows:false
}
}
]
});
}
});
});
How can I fix this?
Update:
What I noticed debugging the code, if I did it correctly:
I set a breakpoint at this call in my addSlider function:
$(target).slick({
the code stops there only once.
When I inspect target at this point it has only one child Item.
maybe this causes the problem
But I don't understand it, I was thinking afterRender
should fire after everything is being rendered, or every time a new child is being added, not just once in the middle of everything.
If I see this correctly I am back to beginning with the question:
How can I execute JS(jQuery) after the uiComponent has fully rendered.
Talking about initial rendering at least, I do understand the concept of data-binds to dynamically update the content but this is not really a concern in this case, since the recently viewed list doesn't update as long as the customer stays on one page.