Ionic4 React framework - IonSlides is not populating pagination - why is params.pagination.el undefined? - reactjs

I have a simple webapp built with the React build of Ionic4. Two of the 'sections' in the app are comprised of full screen IonSlides, with pagination (pager={true}).
In all browsers, except iPad Air Safari, the pagination is populating correctly and the pagination bullets appear. However, in iPad Safari, there is an empty pagination node (no bullet elements have been populated).
I expect it is a race condition but I can't pinpoint where or what is causing this to occur. When I debug and trace the properties of the slides component (which is an instance of Swiper JS) I see that swiper.params.pagination.el is undefined.
Can anyone help me figure out why it's undefined and/or how to correct?
I have tried dynamically adding the slides after componentDidMount. I have tried calling swiper.update() in componentDidMount as well as when the component enters view. I have tried calling swiper.pagination.update() and other resetting/re-updating methods within swiper.
Here's my stripped down/relevant code.
In the view class, declare the slider ref -->
private slider = createRef<any>();
In the component render() -->
<IonSlides ref={this.slider} pager={true}>
<IonSlide className="twocol">
content
</IonSlide>
<IonSlide className="twocol">
content
</IonSlide>
<IonSlide className="twocol">
content
</IonSlide>
<IonSlide className="twocol">
content
</IonSlide>
<IonSlide className="twocol">
content
</IonSlide>
</IonSlides>
In ionViewDidEnter, some of what I've tried (commented out just for reference) -->
ionViewDidEnter() {
this.slider.current.getSwiper().then((swiper:any) => {
// swiper.init();
// swiper.pagination.type='bullets';
// swiper.pagination.el='.swiper-pagination';
// swiper.pagination.init();
// swiper.pagination.render();
// swiper.pagination.update();
swiper.update();
swiper.slideTo(0,0);
}).catch((err:any) => {});
}
I get no error messages. The pagination is just an empty node. Only in iPad Safari. In all other browsers the pagination element contains the appropriate pagination-bullet nodes.

Ok, well I kept at it and I have a solution that works... not sure it's actually solving the underlying problem because I still can't figure out what the underlying problem is... But, posting a solution here in case it's useful to others someday...
I ended up giving a value to params.pagination.el and then init, render, and updating the pagination via the swiper instance to reinstantiate it.
Code used:
this.slider.current.getSwiper().then((swiper:any) => {
swiper.params.pagination.el = '.swiper-pagination';
swiper.pagination.init();
swiper.pagination.render();
swiper.pagination.update();
swiper.update();
}).catch((err:any) => {});

Related

React keeps children from previous render

I have problem rendering page based on array of templates stored in redux. I'm unable to provide the actual code as it is too complex to reproduce in sandbox, but I will try with simplified structure.
I have pinterest-style grid based on flex-grow and js calculations for each item based on its dimensions so I need to have all of this inside one container.
I have 3 groups of items in same div:
blank_item + templates.map + tailItems.map
<div className="grid">
{shouldRenderBlankItem && <BlankItem />}
{templates.map((template) => (
<Template
key={template.uuid}
template={template}
/>
))}
{shouldRenderTail &&
tailItems.map(item, i) => (
<TailItem
key={i}
item={item}
/>
))}
</div>
The problem is - sometimes after render I have EXTRA children left from previous render and React puts them in front of other elements within div.grid so the result I have will look like:
3-4 of EXTRA <Template/> + blank_item + templates.map + tailItems.map
As a key for Template I use template.uuid that is coming from backend and I know they're unique and react also doesn't show any warnings for duplicated keys - so I know I'm not getting any duplicated items from API what I thought might be an issue.
Redux is fine - I see correct number of templates in it e.g. 50, grid and react dev tools shows same 50 templates coming as a prop when I inspect parent component, but the actual DOM has e.g. 53 elements in the same moment.
How can I debug issue like this? I suppose something is wrong during reconciliation but I don't know where exactly to start debugging this.
I have React/ReactDOM 16.13.1
Ok so in my case the problem was broken backend api which might return same template twice - so uuid's that I use for keys were unique for each template but they are not really unique in terms of the page and DOM elements.
I faced this only in production build that is why I didn't have duplicated key warning (on dev build I have another DB with much less templates so I was unable to reproduce this).
So anyone facing similar issue: go check your keys are really unique within page, because what happens after new templates load comes:
React wants to remove old item
It searches corresponding DOM element for each key and finds item with key="XXX" and removes it from DOM
Their duplicated items stays in DOM as corresponding key was processed and React doesn't care about this particular key anymore
We load new templates and they get appended to the parent container
Voila! We have old items before newly loaded
What I did here - my key now is:
templates.map((template, i) => <Template key={template.uuid + i} />
So in this case I am safe even if backend returns duplicated items.
Of course we will fix backend as well. Thanks for reading and helping!
Just quick example how to search for items with same id in data:
var duplicates = new Set();
data.map(t => t.id).forEach((id, i, arr) => {if(arr.filter(item => item == id).length > 1){
duplicates.add(id);
}});
console.log("DUPLICATED IDs:", duplicates)

Leaflet map not properly displayed in ionic react app [duplicate]

I am using tabs to display clear content, but one of them stopped downloading well since it is in the data-toggle tab. It is a Leaflet map.
Here is the code :
Navbar code :
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#home">Données principales</a></li>
<li><a data-toggle="tab" href="#carte">Carte</a></li>
</ul>
<div class="tab-content">
<div id="home" class="tab-pane fade in active">Lorem ipsum</div>
<div id="carte" class="tab-pane fade"> **//see script below\\** </div>
</div>
Script :
<div id="carteBenef"></div>
<script type="text/javascript">
$(document).ready(function () {
var map = new L.Map('carteBenef');
var cloudmadeUrl = 'http://{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
subDomains = ['otile1', 'otile2', 'otile3', 'otile4'],
cloudmadeAttrib = 'Data, imagery and map information provided by MapQuest, OpenStreetMap and contributors, CC-BY-SA';
var cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttrib, subdomains: subDomains});
var iades = new L.LatLng(<?php echo $beneficiaire->latitude . ', ' . $beneficiaire->longitude; ?>)
map.addLayer(cloudmade).setView(iades, 15);
var benefLocation = new L.LatLng(<?php echo $beneficiaire->latitude . ', ' . $beneficiaire->longitude; ?>);
var benef = new L.Marker(benefLocation);
map.addLayer(benef);
benef.bindPopup("<?php echo htmlspecialchars($beneficiaire->nom) . ' ' . htmlspecialchars($beneficiaire->prenom); ?>").openPopup();
});
</script>
The map was appearing well before I put it in the tab, does someone have an idea why it does not work now? Thank you =)
Welcome to SO!
If your Leaflet map suddenly works correctly after you resize your browser window, then you experience the classic "map container size not valid at map initialization": in order to work correctly, Leaflet reads the map container size when you initialize the map (L.map("mapContainerId")).
If your application hides that container (typically through CSS display: none;, or some framework tab / modal / whatever…) or later changes its dimensions, Leaflet will not be aware of the new size. Hence it will not render correctly. Typically, it downloads tiles only for the fraction of the container it thinks is shown. This can be a single tile in the top left corner in the case of a container that was entirely hidden at map initialization time.
This mistake often arises when embedding the map container in a "tab" or "modal" panel, possibly using popular frameworks (Bootstrap, Angular, Ionic, etc.).
Leaflet listens to browser window resize event, and reads again the container size when it happens. This explains why the map suddenly works on window resizing.
You can also manually trigger this update by calling map.invalidateSize() when the tab panel is displayed (e.g. add a listener on the tab button click), at least the first time the container is rendered with its correct dimensions.
As for implementing the tab button click listener, perform a new search on SO on that topic: you should have plenty resources available for that matter, for most of the popular frameworks.
First, thank you #ghybs for your good explanation on why the Leaflet maps are not shown properly in these cases.
For those who tried unsuccessfully the #ghybs's answer, you should try to resize your browser instead of calling a method of the map object :
window.dispatchEvent(new Event('resize'));
As #MarsAndBack said, this fix may cause issues on pan/animation/etc features that Leaflet's invalidateSize provides.
I have this problem because i used modal bootstarp. and it not solved anyway. i tried map.invalidateSize() and window.dispatchEvent(new Event('resize')); but not fixed.
finaly it fixed by this:
$('#map-modal').on('shown.bs.modal', function(event) {});
'shown.bs.modal' event means when modal is completely load and not any confuse on size, now inside that write your codes.
For those like me weren't able to solve the challenge with the precious #ghybs explanation (map.invalidateSize()) and were able to do it with #gangai-johann one, there's still a chance you may do it the right way. Put your invalidate instruction inside a setTimeout, as it may be too early to dispatch that instruction.
setTimeout(() => {
this.$refs.mymap.mapObject.invalidateSize()
}, 100)
Refer to https://stackoverflow.com/a/56364130/4537054 answer for detailed instructions.

how to set a text for a group in vis.js using groupTemplate

I have an application and using vis.js to build timeline events.
I'm using React.
I want to add an icon near each event, so I using groupTemplate and I want to render there a component with group text and icon.
in the options I add:
groupTemplate: (item, element) => {
if (!item) return;
ReactDOM.render(<TimelineGroupComp item={item}/>, element);
}
TimelineGroupComp component render function:
return (
<div className="primary-wrapper" key={this.props.item.content}>
<div style="background-image:url(../images/myIcon.svg);"/>
</div>
);
I get the correct Icon but the text is the item.id instead item.content.
I try to add a span with the correct text in the TimelineGroupComp but it renders also the item.id, not only the correct text.
Also I have another problem: when I collapse/expaned the group, I have an error in the console:
Warning: render(...): It looks like the React-rendered content of this
container was removed without using React. This is not supported and will
cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a
container.
and the group is render normal, ignored my component.
I know this post is almost a year old, but I thought I'd post the answer to this question anyways for anyone who gets the same issue in the future. I've had the same issue and this is how I fixed it.
Starting with React 16, if you want to use React templates in Vis Timeline you'll have to use React.createPortal in the groupTemplate option, see https://jsfiddle.net/api/post/library/pure/.
For the code above, the correct groupTemplate would be:
groupTemplate: (item, element) => {
if (!item) return;
ReactDOM.createPortal(
ReactDOM.render(<TimelineGroupComp item={item}/>, element),
element
)
}

Why does React functionality not working on Chrome and Opera?

I made a Single Page Application with React and everything was fine until I tested it on all the major browsers.
I used Mozilla during the development but then I tried it on other browsers and it worked on Edge, Explorer and Mozilla but it didn't on Chrome and Opera. All of the UI stuff works fine, and everything design related behaves well except the functionality that I made with React. So clicking on the Meal Types on the options dropdown, another list is supposed to appear on the page replacing the initial one with those 3 sections(Blog, Recipe, Tips). The application is supposed to expand further again by clicking on the Cookie section and again by clicking on the Horse section. As I said, all of this works fine except in Chrome and Opera. I want to make React behave as I described in Chrome and Opera too.
This is a link to the files
https://github.com/cristiAndreiTarasi/React_App
I expect that on clicking on the Meal Type option the app make those additional sections appear below - as it does in other browsers - in Chrome and Opera browsers and find what the problem is.
In your Header.jsx and Footer.jsx files you use class instead of className a few times, so you should correct that: https://reactjs.org/docs/faq-styling.html
Your problem is that you are registering an onClick on the <option> element, when you should be using the onChange prop on the <select> element itself, not the <option> element.
<select onChange={this.handleChange} className="custom-select mb-3">
<option selected>All</option>
<option value="mealType">Meal Type</option>
<option>Ingredients</option>
<option>World Cuisine</option>
<option>Cooking Style</option>
</select>
Your event handler looking like this:
handleChange = (event) => {
this.setState({
[event.target.value]: true,
});
}
Your state shouldn't be holding entire React components like it currently is, instead try something like this:
state = {
mealType: false,
}
With your render looking like this:
{this.state.mealType && <MealType click={this.handleClickTwo}/>}
As a general rule: state should contain data that a component's event handlers may change to trigger a UI update. this.state should only contain the minimal amount of data needed to represent your UI's state. See here for more details: http://web.archive.org/web/20150419023006/http://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html

ng-srcset images initially not displaying in IE11 intermittently

The page loads without any of the images displaying on IE11 only, but refreshes them accordingly when we resize the browser intermittently (1/3 loads). We cannot replicate this with any of the other browsers. srcset works fine by itself with static content.
Here is a Plunker example of it not working in IE11.
Or quick and easy, the actual img html we're using:
<img data-ng-srcset="{{::image.url}}, {{::image.url2x}}" alt="{{::image.name}}"/>
The images or surrounding divs do not have any transitions, shadows or opacity applied.
The html renders fine with angular passing over and rewriting the srcset attribute correctly. The images just do not appear, only the alt tag. Wondering if this could be a call stack issue due to the intermittence of it, maybe a race condition with Picturefill loading before angular finishes a digest or something.
Cheers in advance!
A work around if you use PictureFill in a loop and in a specific case (not on all images of your application), is calling a function that launch PictureFill directly from HTML, after last item loaded (this is not the best practice but fix the IE11 problem) :
<picture><!-- Your image --></picture>
<span ng-if="$last">
{{ controllerAlias.launchPictureFill() }}
</span>
Came across this as a solution: http://tech.endeepak.com/blog/2014/05/03/waiting-for-angularjs-digest-cycle/
var waitForRenderAndDoSomething = function() {
if($http.pendingRequests.length > 0) {
$timeout(waitForRenderAndDoSomething); // Wait for all templates to be loaded
} else {
$window.picturefill();
}
}
$timeout(waitForRenderAndDoSomething);
The only issue that the blog post describes is here, so if anyone has anything better please let me know:
The $http.pendingRequests supposed to be used for debugging purpose only. If angular team decides to remove this, you can implement the same using http interceptors as suggested in this link.

Resources