Fix double componentDidMount in React when add jQuery carousel - javascript

When I would like to add an old jQuery carousel (jCarousellite) into my React project I have faced with double componentDidMount update in case when I work within React component. Such behaviour of React produces by the dynamical insertion of additional tags (carousel items) into the carousel cause It needs It to some functionality like circular switching. So I have found only one working relatively good solution just to copy the embbed into the main container of the carousel tags to some local variable and use the variable in componentWillUnmount like this
import $ from "jquery"
window.jQuery = $;
require("jcarousellite/jcarousellite");
class Footer extends Component {
main_slider_items; //variable to copy the carousel items into
componentWillUnmount() {
$('.brand_slider ul').html(this.main_slider_items); //insert into the carousel saved during the first mount items
}
componentDidMount() {
this.main_slider_items = $('.brand_slider ul li'); //save items of the carousel here
$('.brand_slider').jCarouselLite({ //assign jQuery plugin to the element
btnNext: '.brand_slider_wrap .next',
btnPrev: '.brand_slider_wrap .prev',
mouseWheel: true,
visible: 4,
scroll: 1,
speed: 150,
circular: true
});
}
render() {
(
{/* some render code here */}
)
}
...
Either If I would like to use indicators functionality of my carousel for instance then I have to do the same trick with them otherwise they also would have been mounted twice and the second time It would be the wrong value. Unfortunalety that solution has at least one disadvantage - items switching within unpredictable movement sometimes but the order of the switching is right. I tried to suppress It somehow by decreasing the speed of the switching but nevertheless I just wonder If there another efficient solution cause sometimes I need to work with very legacy projects and plenty of old code such as various jQuery plugins those wouldn't be rewritten to React style immediately. Thank you.

Related

jquery owl-carousel owl-dots class issue

I get some problem when I use owl-carousel in my Rails project.
when I go back to the cached page of my browsers which is using carousel class, I get too many carousel owl-dot classes in my page,this is my
JS code
function initScrollboxHobby() {
var owl = $(".owl-carousel");
owl.owlCarousel({
// loop: true,
items: 1,
nav: true
});
}
and issue HTML code
How to fix it ?
I guess you are using turboinks and when you go back the page is cached by it and when it loads it reruns the owl carousel init function.
The problem, básically, is that turbolinks doesn't play well with non-idempotent functions https://github.com/turbolinks/turbolinks#making-transformations-idempotent
I've managed to make a workaround for this problem with other js plugins, so it should work for you.
Básically, the idea is:
First, when the user enters the page for the first time, copy the content of the element with class .owl-carousel on itself as a data attribute
carousel = $('.owl-carousel');
carousel.data('originalHtml', carousel.html();
carousel.owlCarousel(....)
Then, when the users goes back, before initializing the carousel, check if it was initialized and cached, in that case, first replace the content with the original and remove classes
carousel = $('.owl-carousel');
if (carousel.hasClass('owl-loaded')) {
carousel.html(carousel.data('originalHTML')).removeClass('owl-theme owl-loaded owl-drag');
}
carousel.owlCarousel(....)
You can mix both steps into one:
$(function() {
const carousel = $('.owl-carousel');
if (carousel.hasClass('owl-loaded')) { //if it has the class then it means it's the cached view
carousel.html(carousel.data('originalHTML')).removeClass('owl-theme owl-loaded owl-drag');
} else { // else it's a fresh load of the page
carousel.data('originalHtml', carousel.html());
}
carousel.owlCarousel(....)
})
It's a little bit hacky, but the only way I found to use plugins that are not prepared to work with turbolinks without going through modifying those plugins.
Another option would be to just disable Turbolinks if you thing you just don't need it.
For arieljuod's help of problem reason and my own trying.
I coded like this now.
var owl = $(".owl-carousel");
var owl_navs = $('.owl-carousel .owl-nav');
var owl_dots = $('.owl-carousel .owl-dots');
var owl_cloned = $('.owl-carousel .owl-stage-outer .owl-stage .cloned');
if(owl.hasClass('owl-loaded'))
{
owl_navs.remove();
owl_dots.remove();
owl_cloned.remove();
}
owl.owlCarousel({
loop: true,
items: 1,
nav: true
});
It's tedious but worked well.
Now,I understanded reason.
When I go back to cached page,because I wrote javascript code in my ERB file,in that way, old HTML code may changed.
And then Turbolinks function will run the JS code in that CHANGED new HTML code,Turbolinks must do that,because when I visit cached page again,it will lose all the event binds.
So the whole carousel items will be messy.

Ionic Dragula, setOptions mirrorContainer is null. Loading before DOM

I have an ionic project that is using dragula, but I'm having an issue setting the mirrorContainer. I'd like to make the container something other than the default body because I believe that's what is attributing to a strange scrolling problem I'm having while dragging.
I've created my bag in html
<div class="step-container--line" [dragula]='"bag"' id="mirror">
<div class="card">
....
</div>
Then in the JS, I've initialized dragula in the constructor and started to set its options.
constructor(private dragulaService: DragulaService) {
dragulaService.setOptions('bag', {
moves: function (el, container, handle) {
return handle.className === 'step__menu__button';
},
direction: 'vertical',
//mirrorContainer: document.getElementById('mirror')
});
dragulaService.drag.subscribe((value) => {
this.onDrag(value.slice(1));
});
dragulaService.drop.subscribe((value) => {
this.onDrop(value.slice(1));
});
}
The problem is; when I add mirrorContainer: document.getElementById('mirror') to the setOptions, my mirror container comes back as null. I'm assuming because this loads before the DOM does and there's no instance of #mirror yet.
If I moved everything down into ionViewDidLoad(){}, I get an error stating that the bag 'bag' already exists.
I'm not sure the best way to initialize or add to the options after the DOM loads. Any ideas?
I know I'm late to the party but I had the same issue using Angular 11.
After your element is in the DOM and is available you can do this.
ionViewDidLoad() {
(this.dragulaService as any).groups['bag'].options.mirrorContainer = document.getElementById('mirror');
}

GridStack- Setting the static grids

I am trying to make the grids static. No movement at all.
I tried:
var options = {
staticGrid: true,
};
$('.grid-stack').gridstack(options);
and also this
var options = {
setStatic: true,
};
$('.grid-stack').gridstack(options);
and this
var options = {
staticGrid: true,
};
$('.grid-stack').gridstack(options);
$('.grid-stack').data('gridstack').setStatic(true);
None of them seems to work, I used this link for documentation.
They have also mentioned a method setStatic but there are no examples of usign this method.
According to the Gridstack docs the staticGrid:true parameter is correct if you want to initialise and define the grid as STATIC at startup (your first method).
The SetStatic(true) is a function you can call on for toggling this state programatically.
If you view the source code live you will see a new CSS class has been added to the grid wrapper DIV; a class called 'grid-stack-static'. The appearance of this class confirms the parameter option staticGrid:true has been accepted and actioned.
BUT as I found myself (with v0.30 of the library), grid widgets in my initialised grid are still resizable and movable. In my opinion this suggests a bug.
You can lock down movement and resizing at a widget item level by using the item attributes data-gs-no-resize="yes" and data-gs-no-move="yes".
Seems counterproductive to have to do this if you have said 'static' already.
I have lodged an issue on Github to query this behaviour.
BTW it has been suggested to call on and use the setStatic( true ) function after grid init; as a temporary fix for this bug. Which was your third method - AND this worked for me.
Only difference between your 3rd method and mine is the function is wrapped in a document.ready function (and I am using $=jquery shortcut for convenience/compatibility on my system).
Worked:
(function ($) {
// Shortcut $=jquery
$(document).ready(function () {
// start grid
$(function () {
var options = {
staticGrid:true
};
$('.grid-stack').gridstack(options);
$('.grid-stack').data('gridstack').setStatic( true );
});
// END DOC READY
});
// SHORTCUT FIX
})( jQuery );
Just set the attribute gs-static="true"on the grid-stack's parent Grid element (on which gridstack has been initialized).
<div class="grid-stack" gs-static="true">
<div class="grid-stack-item">
<div class="grid-stack-item-content">Item 1</div>
</div>
<!-- .. and so on -->
</div

iDangero Swiper using Worklight?

Is there an easy way to implement this? I am having trouble getting it to work properly. I tried following the instructions on the website to have it load at window.onload, but Worklight seems to fire that call before the DOM is even visible, so I couldn't do that. I have some initialization code and so I am calling the following method in that code:
function runSwiper(){
// iDangerous Swiper
var mySwiper = new Swiper('.swiper-container', {
pagination : '.pagination',
loop : true,
grabCursor : true,
simulateTouch : true,
paginationClickable : true
});
}
Yet, all I see in my carousel is just Text. I have two test slides in there currently, but the slide doesn't even seem to work on my Android emulator. Is there anything else I need to be doing for this to work properly? If not, is there something that works as nicely as this that will play nice with Worklight?
I am using worklight version 6.2. I am usingth is: http://www.idangero.us/sliders/swiper/api.php
What I am aiming to do is fade out one div that has information, and fade in another div that has more info and the Swiper present, so initially this swiper will be hidden.
I turns out, in my case, the easiest way to call the swiper to initialize was later on (being that it was in a hidden div) instead of at window.onload. In addition, I had some issues with slides disappearing, which I fixed by having the slides pre-defined in the HTML instead of adding them dynamically.

Ext.layout.CardLayout requires fullscreen?

I'm programming a Sencha Touch app with a moderately complex composition of Ext.TabBar with Ext.Panel 's inside it.
But my Ext.Panel using Ext.layout.CardLayout runs in to a mysterious problem when not having a fullscreen: true property set on it: it throws an TypeError: Object card has no method 'setActiveItem' when I try to call the panel's .setActiveItem() method which it didn't in my proof of concept version that had fullscreen: true turned on.
I can replicate the problem on Chrome's console at a page with the Sencha Touch library loaded like this:
> var p1 = new Ext.Panel({layout:'card', items:[{html:'a'},{html:'b'}]})
undefined
> p1.setActiveItem(0)
TypeError: Object card has no method 'setActiveItem'
And it doesn't happen with the .fullscreenproperty:
> var p2 = new Ext.Panel({fullscreen: true,
layout:'card',
items:[{html:'a'},{html:'b'}]})
undefined
> p2.setActiveItem(0)
subclass
What gives?
Version info: I'm using Sencha Touch 1.0.1a
Update 1 (Jan 3, ~10.30UTC+1h), stepping around with the debugger and discovering things:
Just setting layout: 'card' won't trigger the creation of an actual Ext.layout.CardLayout object being created for real. Since .setActiveItem() tries to delegate to the compent's .layout property, it will fail almost instantly. However, setting .layout to a new Ext.layout.CardLayout causes more problems down the line..
Update 2: (Jan 3, ~12:25UTC+1h) It all comes down to various component objects not being rendered/inserted in the dependency sufficiently to be ready to render. I managed to get my code working by adding listeners, first a listener for the added event in the enclosing panel that does a this.setLayout(new Ext.layout.CardLayout());, then an afterrender listener on the component being added that finally calls .setActiveItem() to switch to the desired card.
A card layout works fine when it's not the outer, fullscreen one, but of course something has to be the root, fullscreen panel.
In this case, I'm using a 'fit' layout around the inner card layout, and setting the active item works fine:
var inner = new Ext.Panel({
layout:'card',
items:[
{html:'a'},
{html:'b'}
]
});
var outer = new Ext.Panel({
fullscreen:true,
layout:'fit',
items:[
inner
]
});
I suspect the matter is more about whether the panel has been rendered or not, and fullscreen:true just happens to force immediate rendering (and of any children, which is why it works in my code above).
This is from the Ext.Component source:
if (this.fullscreen || this.floating) {
this.monitorOrientation = true;
this.autoRender = true;
}
if (this.fullscreen) {
var viewportSize = Ext.Viewport.getSize();
this.width = viewportSize.width;
this.height = viewportSize.height;
this.cls = (this.cls || '') + ' x-fullscreen';
this.renderTo = document.body;
}
I suppose you could do some of this set up manually... but I have to ask, how do you avoid have a fullscreen ancestor component outside it in the first place?
I had the same problem. The card layout only works if the container panel had the layout of 'fit'. But setting the container class to fit was causing the scroller to not working properly. So what I had to do is disabling the scroller for container and the card panel and set the scroller for the children of card panel.
Update 2 in my question kind of answers the question, although the solution feels distinctly cargo-cultish. I won't be too surprised if it breaks in future Sencha Touch versions.
Sencha may have changed this since this was posted since I don't get an error. However, setActiveItem() will force my views to be rendered immediately which is undesirable when setting up the UI. If you want to set the initial card without rendering the views immediately, use the property directly instead of the setter, like this:
yourComp.activeItem = yourView;
instead of
yourComp.setActiveItem(yourView);

Categories

Resources