ngx-slick-carousel reinitialize slick after unslick in Angular - javascript

I am using Angular 7.
I added ngx-slick-carousel as described here: https://www.npmjs.com/package/ngx-slick-carousel.
I adjusted slideConfig the way I want:
slideConfig = {
slidesToShow: 3,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 1000,
speed: 1400,
infinite: true,
lazyLoad: "ondemand",
responsive: [
{
breakpoint: 991,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
autoplaySpeed: 2000,
speed: 100
}
},
{
breakpoint: 575,
settings: unslick
}
]
};
I want to unslick on viewport less than 575px so I added this in slideConfig. Configuration parameters are explained here: https://kenwheeler.github.io/slick/
Now, if viewport gets back greater than 575px I want to re-activate the slick.
None of the solutions provided online worked.
Here for example, when I created the ViewChild, console prints undefined.
Reference slick instance from component (ngx-slick-carousel)
I referred also to solutions where jQuery is used to enabled slick again, but the jQuery functions are not identified in Angular.
https://github.com/kenwheeler/slick/issues/1730

The slick carousel element in html has a property #slickModal="slick-carousel".
In our component, we import the SlickCarouselComponent
import { SlickCarouselComponent } from "ngx-slick-carousel";
and refer to the slick carousel in a variable
#ViewChild("slickModal") slickModal: SlickCarouselComponent;
now we import the HostListener
import { HostListener } from "#angular/core";
and in the following function, we detect the width of the viewport. If the viewport is greater than the unslick breakpoint, we re-initialize the slick.
#HostListener("window:resize", ["$event"])
getScreenSize(event?) {
if (this.slickModal !== undefined) {
if (window.innerWidth > 575) {
if (!this.slickModal.initialized) {
this.slickModal.initSlick();
}
} else if (this.slickModal.initialized) {
this.slickModal.unslick();
}
}
}
make sure to call this function in the constructor of your component
constructor() {
this.getScreenSize();
}

Related

Reset slide to the initial slide after images changes on swiper for angular

Scenario :
When the page is rendering the products are shown and every product has a discover button. On clicking the button I want to display the gallery / slider images below that product.
Currently, when I click the first product, Swiper is showing the images. And, if I slide the images to 3rd or 4th (or any), the new product slider image is starting from that particular index i.e., from 3rd or 4th.
The configuration I used for Swiper is :
public config: SwiperConfigInterface = {
centeredSlides: true,
initialSlide: 0,
slidesPerView: 1,
loop: false,
spaceBetween: 0,
autoplay: false,
direction: 'horizontal',
keyboard: false,
navigation: true,
pagination: {
el: '.swiper-pagination',
type: 'bullets',
clickable: true
},
hashNavigation: false,
effect: 'slide'
};
And, the discover button is calling the below method :
discover(locationId) {
this.locationImages = [];
this.destinations.forEach(selectedData => {
if(selectedData._id == locationId){
selectedData.optional_images.forEach(image => {
this.locationImages.push(image)
});
}
});
console.log(this.locationImages);
}
I searched the docs and google, didn't find any reliable solution. Please suggest some answers. Thank you in advance.
Check the documentation at https://swiperjs.com/swiper-api - seems that you can use swiper.slideTo(index, speed, runCallbacks) method to revert to first (or any) slide element whenever you need, like upon changing the active product.

Scrollify (jquery) - disable plugin on some pages at the site (newbie)

I am trying to use scrollify for my website (Wordpress, woocomerce) www.chame-lemon.com and I'm totally green in programming, so I really need your help guys.
I need to disable the plugin on shop page and product pages, I'm using a class named "hwdp" to all sections on the pages when I want to use plugin. but he is activated on other pages because of the footer (it has a class to turn on scrollify also) but I can't use two separate footers in Wordpress, so I need to use code with using a function
$.scrollify.disable();
The disable method turns off the scroll snap behavior so that the page scroll like normal.
there is documentation for that plugin
https://projects.lukehaas.me/scrollify/#methods-continued
that should look like that:
if there is no class named hwdp on the page
the plugin should be disable
else
he should be enabled
and I tried to fix that by myself, I spend hours and i got no results... and i know that's a very simple thing for someone who knows jquery.
<script>
jQuery(document).ready(function($) {
$.scrollify({
section : ".hwdp",
interstitialSection: ".footer",
easing: "easeOutExpo",
scrollSpeed: 1200,
offset: 1,
scrollbars: true,
standardScrollElements: "",
setHeights: true,
overflowScroll: true,
updateHash: true,
touchScroll: false,
before:function() {},
after:function() {},
afterResize:function() {},
afterRender:function() {},
});
if (!$('section').hasClass('.hwdp')) {
$.scrollify.enable();
}else{
$.scrollify.disable();
}
});
</script>
In your code, the plugin is being initialized on every page regardless of whether it finds the .hwdp class. It's better to only be initialized when it needs to be.
Here's how you can enable the plugin only when there exists a section on the page with the class .hwdp.
<script>
jQuery(document).ready(function($) {
if($('section.hwdp').length) {
$.scrollify({
section : ".hwdp",
interstitialSection: ".footer",
easing: "easeOutExpo",
scrollSpeed: 1200,
offset: 1,
scrollbars: true,
standardScrollElements: "",
setHeights: true,
overflowScroll: true,
updateHash: true,
touchScroll: false,
before:function() {},
after:function() {},
afterResize:function() {},
afterRender:function() {},
});
}
});
</script>

Javascript Class Instantiation

I've created a simple javascript class to wrap slick carousel.
What is the best way to instantiate multiple instances of this class?
I've currently got the below, which search the DOM and creates a new class instance of the component, if found. I suspect my project will grow a little and this 'root' file may because a little bloated/confusing. Is there a better way to organize it?
I'm going to be adding additional classes for further functionality, so trying to find the best approach early.
main.js
import Carousel from '../app/assets/javascripts/carousel/Carousel';
var carouselElements = Array.from(document.getElementsByClassName('slick-media'));
if (carouselElements) {
carouselElements.map(function (element) {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
}
Carousel.js
export default class Carousel {
constructor(element, variableWidth = false, breakpoint) {
this.element = element;
this.variableWidth = variableWidth;
this.breakpoint = breakpoint
this._init(element);
/* Bind methods */
this._init = this._init.bind(this);
}
_init() {
this._instantiateSlick(this.element);
}
_instantiateSlick(element) {
$(element).slick({
lazyLoad: 'ondemand',
slidesToScroll: 1,
adaptiveHeight: true,
useTransform: true,
/* Default over lg breakpoint */
slidesToShow: this.breakpoint.lg,
/* Allow slick to calculate widths */
variableWidth: this.variableWidth,
responsive: [
{
breakpoint: 1199,
settings: {
slidesToShow: this.breakpoint.lg,
slidesToScroll: 1,
infinite: true
}
},
{
breakpoint: 991,
settings: {
slidesToShow: this.breakpoint.md,
slidesToScroll: 1,
infinite: true
}
},
{
breakpoint: 480,
settings: {
slidesToShow: this.breakpoint.xs,
slidesToScroll: 1,
infinite: true
}
}]
});
}
}
You've clarified that your question is about how you're using your class (e.g., the code in main.js):
it's about the main.js file, and the way it's instantiating multiple instances. Is this approach the best possible?
There are a couple of nits to pick:
You're using map to create an array you fill with undefined and never use. When you're not using the returned array, don't use map; use forEach of any of several other ways to loop over arrays and array-like structures.
There's no need for the if; methods like getElementsByClassName always return a NodeList or HTMLCollection (depending on the method), even if it's empty.
Also, is it scalable in a big project, where there will be multiple classes, this file is going to become pretty unreadable?
I don't see any reason for it to become unreadable. If you want to stick to getElementsByClassName (because it can be amazingly fast; note that it doesn't exist on obsolete browsers like IE8 though):
import Carousel from '../app/assets/javascripts/carousel/Carousel';
const forEach = Function.call.bind(Array.prototype.forEach);
const hookUpClass = className => {
forEach(document.getElementsByClassName(className), element => {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
};
hookUpClass('slick-media');
hookUpClass('another-class');
hookUpClass('some-further-class');
Note the use of Array.prototype.forEach to do the looping for us, since it'll work on any array-like thing. That will work on any modern browser and IE9-IE11.
If you don't mind a very very very very small bit of overhead, you can use querySelectorAll (which exists even on IE8) to get a list matching all of your classes, rather than having to handle each class separately:
import Carousel from '../app/assets/javascripts/carousel/Carousel';
document.querySelectorAll('.slick-media, .another-class, .some-further-class').forEach(element => {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
I didn't use Array.prototype.forEach there because NodeList has its own forEach which does the same thing. If you need to support IE9-IE11, you'll want the polyfill, which is trivial:
if (typeof NodeList !== "undefined" &&
NodeList.prototype &&
!NodeList.prototype.forEach) {
// Surprisingly, the standard `NodeList.prototype.forEach` is enumerable (as well as
// writable and configurable) so we can just assign rather than using `defineProperty`
NodeList.prototype.forEach = Array.prototype.forEach;
}
Or of course, use Array.prototype.forEach instead as we did with getElementsByClassName if you prefer.
In cutting-edge environments like the latest Chrome and Firefox, you could do this:
import Carousel from '../app/assets/javascripts/carousel/Carousel';
for (const element of document.querySelectorAll('.slick-media, .another-class, .some-further-class')) {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
}
...which relies on the NodeList being iterable. I didn't do that above because polyfilling an iterator on NodeList is harder than polyfilling forEach without knowing what transpiler you're using (if you're using one).
Using a class for this seems overkill to me. You are never accessing the created objects later, as you don't you keep a reference to them, so their sole purpose seems to be to perform one immediate action: apply slick to elements with some predefined configuration. After their construction they immediately become useless.
Secondly, using a default value for a parameter is not very useful when after that you still have a parameter that turns out to be mandatory. So swap the parameter positions of breakpoint and variableWidth.
As an alternative I would suggest creating a jQuery plug-in for this:
$.fn.simpleSlick = function (breakpoint, variableWidth = false) {
this.slick({
lazyLoad: 'ondemand',
slidesToScroll: 1,
adaptiveHeight: true,
useTransform: true,
/* Default over lg breakpoint */
slidesToShow: breakpoint.lg,
/* Allow slick to calculate widths */
variableWidth,
responsive: [{
breakpoint: 1199,
settings: {
slidesToShow: breakpoint.lg,
slidesToScroll: 1,
infinite: true
}
}, {
breakpoint: 991,
settings: {
slidesToShow: breakpoint.md,
slidesToScroll: 1,
infinite: true
}
}, {
breakpoint: 480,
settings: {
slidesToShow: breakpoint.xs,
slidesToScroll: 1,
infinite: true
}
}]
});
return this; // to allow chaining
};
And assuming that the slick plugin plays according to the rules (i.e. allows the jQuery object to match more than one element), your actual use of it becomes as easy as:
$('.slick-media').simpleSlick({xs: 6, md: 6, lg: 6 }, true);
When the class is a given
If the use of a class should be taken as a given, then you could still use the jQuery way for selecting the elements by their class attribute (since you already use jQuery for the slick plugin):
$('.slick-media').each(function () {
new Carousel(this, true, {xs: 6, md: 6, lg: 6 });
});
(But again, applying new without using the resulting object reveals a wrong design)
And if you want to access these Carousel objects later via the DOM element, then you could consider using the data method:
$('.slick-media').each(function () {
$(this).data('carousel', new Carousel(this, true, {xs: 6, md: 6, lg: 6 }));
});
For a given elem element, you can then access the corresponding Carousel instance as follows:
var carousel = $(elem).data('carousel'); // Get the Carousel instance
You can actually shortify it to:
Array.from(
document.getElementsByClassName('slick-media'),
node => new Carousel(node, true, {xs: 6, md: 6, lg: 6 })
);

Ionic 2 with Reveal.js presentation inside

I'm working in a Reveal.js presentation, for future needs I'm wrapping it around an Ionic 2 app.
First approach is working fine, what I've done is a simple sidemenu template with a page that loads the Reveal.js presentation.
At first it seems to work fine, but:
issue: First time I open the Reveal.js page, it loads ok, but If I'm loading another page, and then returning to it, it doesn't load the presentation.
Example:
https://github.com/xanisu/ionic2-reveal.js
reveal.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import * as Reveal from 'reveal.js/js/reveal';
#Component({
selector: 'page-reveal',
templateUrl: 'reveal.html'
})
export class RevealPage {
reveal : any;
loaded : boolean = false;
onstructor(public navCtrl: NavController) {
}
ngOnInit() {
console.log("ngOnInit!");
this.reveal = Reveal;
this.loadSlides();
}
loadSlides() {
//this function is intended to load all dinamic content for slides (json, bbdd, webservice....)
this.revealInit();
}
ionViewDidLeave() {
this.loaded = false;
}
revealInit() {
this.reveal.addEventListener( 'ready', ( event ) => {
this.loaded = true;
});
let revealOptions = {
controls: true,
progress: true,
slideNumber: false,
history: false,
keyboard: true,
overview: true,
center: true,
touch: true,
loop: false,
rtl: false,
shuffle: false,
fragments: true,
embedded: false,
help: true,
showNotes: false,
autoSlide: 0,
autoSlideStoppable: true,
autoSlideMethod: Reveal.navigateNext,
mouseWheel: false,
hideAddressBar: true,
previewLinks: false,
transition: 'slide', // none/fade/slide/convex/concave/zoom
transitionSpeed: 'default', // default/fast/slow
backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom
viewDistance: 3,
parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px"
parallaxBackgroundHorizontal: null,
parallaxBackgroundVertical: null
};
this.reveal.initialize(revealOptions);
}
}
reveal.html
<div class="reveal">
<div class="slides">
<section>Single Horizontal Slide 1</section>
<section>
<section>Vertical Slide 2.1</section>
<section>Vertical Slide 2.2</section>
</section>
<section>Single Horizontal Slide 3</section>
</div>
</div>
As far as I know, in your Reveal.js version (3.5.0) you can only use the Reveal.initialize() once. This github feature request is the only info I could find about it.
In the 4.0.0 Reveal.js version there is a new feature called Multiple Presentations so now you can create multiple instances of the Reveal class and initialize a new one each time you enter the page.
Even though we don't want to have multiple presentations side to side, this solved my problem when revisiting the same page won't load the Reveal slides again.
A brief example of how you can store your <div class="reveal presentation">...</div> in a new instance of Reveal could be something like this:
ngOnInit() {
console.log("ngOnInit!");
let presentation = new Reveal(document.querySelector('.presentation'));
presentation.initialize();
}

carouFredSel breaking on fade in

I built an image slider using the carouFredSel plugin and everything works fine with one single carousel of images but as I continued building out my site I've reached some problems.
The first visible carousel works fine, but if you click the IMG link on the top right to see the second carousel, it neglects the bit of code telling it to only display one item at a time or something like that.
I've made sure everything is set up the same in both and I've tried experimenting with display and float changes with no luck
I'm also now realizing that it seems to be caused by the fading transition because the same thing happens on the first slider when I set a fade in to that as well.
Guessing it has something to do with the display change that the jQuery script does
http://coreytegeler.com/new/#work
Any ideas??
$("#web-carousel").carouFredSel({
width: "100%",
height: 450,
direction: "right",
circular: true,
infinite: true,
items: {
visible: 1,
width: "variable",
height: 400,
width: 720
},
scroll: {
duration: 1000,
pauseOnHover: true
},
auto: {
timeoutDuration: 9999,
delay: 5000
},
prev: {
button: "#web-left",
key: "left"
},
next: {
button: "#web-right",
key: "right"
},
pagination: {
container: "#web-pagination",
keys: true
},
swipe: true,
mousewheel: false
});
I'm not sure if this is what's causing you trouble, but it sounds like it might be the same issue I ran into. I have my carousel in a hidden div revealed by an animate() triggered by a click. It wasn't rendering correctly until I put the carousel initialization into a post-animate function -- ensuring that the carousel wasn't initialized until the div was ready seemed to be the key, probably because the carousel code looks at the sizing of things when it's preparing the images. So what I did was to wrap the carousel initialization in a function:
var do_carousel = function() {
$('#carousel').carouFredSel({
responsive: true,
circular: false,
etc...
Then in the code that reveals the div, it calls that function:
$("#carousel-div").animate({
opacity: 'toggle',
width: 'toggle',
}, 1200, function() {
do_carousel();
});
Once I moved the carousel initialization there, it started working. I hope perhaps that will help you find the root of your issue.

Categories

Resources