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

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.

Related

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

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) => {});

Angular not updating list fast enough

I have a list of 'cards' that when a filter is selected on the left side of the screen, the cards shown in the middle of the screen will update accordingly. Here is a snippet of the js:
$scope.isReady = false;
// This is an async method to retrieve from DB
AssignmentManagerService.getAssignmentsForClass(teacherId, classId).success(function(response) {
var instances = response.instances;
$scope.assignmentListData = instances;
$scope.isReady = true;
});
And here is a piece of the template:
<div flex class="item-list" ng-if="isReady">
<md-card ng-repeat="assign in assignmentListData track by assign.instanceId"
ng-click="selectCard(assign)"
class="item-list-item"
style="font-size:16px;"
ng-class="getCardClass(assign)"
ng-style="getCardStyle(assign)">
<div>
<span style='font-weight:500'>Title</span>: {{assign.title}}
</div>
<div style="margin-top:10px;">
<span style='font-weight:500' ng-if="assign.availableDate">Available Date:</span> {{assign.availableDate | date:'fullDate'}}
<span style='font-weight:500' ng-if="assign.createDate">Create Date:</span> {{assign.createDate | date:'fullDate'}}
</div>
</md-card>
</div>
One of two things will happen: I will either get two sections of cards on top of each other, one section with the old selection and one section with the new selection, for a half second before the old selection is removed OR the cards will all be integrated before the old cards are removed. All of this happens within the time span of a second but looks very unprofessional and ugly.
What is supposed to occur is the user makes a filter selection, the cards immediately hide, the new data is retrieved, and the new cards are shown. No overlapping, no multiple groups.
Here's what I have done:
Wrapped the async method/success into a $timeout function: Didn't work. Same results.
Wrapped the async method/success into a $scope.$$postDigest function: Didn't work. Same results.
Added a 400ms thread sleep in the back-end: While it does work, I'm not inserting a back-end fix for a front-end problem.
Tried various combinations of ng-if/ng-show on the div and the md-card: Didn't work. Same results.
The template was originally ~500 lines. I have stripped out all the extraneous markup except for this card bit to see if it was the digest cycle taking too long. Didn't work. Same results.
Any help would be greatly appreciated. Thanks!
Wow. My coworker and I just found some really old and buried CSS animations that were affecting the list in question. After removing them, everything works as it should. Doh! I'd like to thank #The.Bear and #JBNizet for pointing me in the right direction on this!

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.

Detecting page scroll/ current section being viewed in AngularJs

My page is divided into sections : #page-1 and #page-2
See Plnkr: http://plnkr.co/edit/RZJLmsWDfs63dC0QuDJi
<body>
<section id="page-1">
This is page 1. It takes the whole height of the browser. User has to scroll down to see page-2.
</section>
<section id="page-2">
<span class="animated bounce">This is page 2 </span>
</section>
</body>
Animation classes are being applied to different elements in #page-2.
However by the time the user scrolls down to these elements, the animation has already finished. Hence they just look like static objects.
Is there anyway I can detect when #page-2 is currently being viewed and then call a function to addClass('animated bounce') to certain elements ?
I would like to achieve this in angularjs if possible
I have found a angularjs directive that is probably helpfull for you in this case. Inview tries to solve this exact problem by reporting to you if a dom element is visible on the screen. Unfortunately I have been unable to test my solution because I couldn't find a minified js file of Inview but I assembled some code that should work:
<section id="page-2" in-view="{$inview ? isFocused=true;}">
<div ng-class="{'animated bounce': isFocused}">This is page 2 </div>
</section>
The $inview is supposed to be true whenever the element is in visible in the browser. This leads to the scope variable isFocused being set to true and therefor the animation class is added to your div.
This should work as you have intended in your question, if it does not work for some reason please let me know so I can improve my answer.

Google translate widget appears twice

I have a responsive site that uses the google translate widget. The weird thing is that for some time the widget now appears twice, and this seem to be related to the responsive design because if I place the same widget code on a simple html page it only appears once. I have no idea on how to solve this. Has anyone come across this?
Update.
I have discovered that this is caused by jquery.themepunch.showbizpro.min.js, if I remove that one the widget only appears once. I have not found a way to fix this yet but there might be a way. I found this piece of code.
<script>
function googleTranslateElementInit() {
new google.translate.TranslateElement(
{ pageLanguage: 'sv' },
'google_translate_element'
);
/*
To remove the "powered by google",
uncomment one of the following code blocks.
NB: This breaks Google's Attribution Requirements:
https://developers.google.com/translate/v2/attribution#attribution-and-logos
*/
// Native (but only works in browsers that support query selector)
if(typeof(document.querySelector) == 'function') {
document.querySelector('.goog-logo-link').setAttribute('style', 'display: none');
document.querySelector('.goog-te-gadget').setAttribute('style', 'font-size: 0');
}
//If you have jQuery - works cross-browser - uncomment this
jQuery('.goog-logo-link').css('display', 'none');
jQuery('.goog-te-gadget').css('font-size', '0');
}
</script>
This code remove the logo, so I'm thinking that if I use javascript I could check and remove duplicate occurrences of <select class="goog-te-combo"> then I would only have one left, is that possible?
This happened to me using Bootstrap. I had two instances of the Google Translate code - one instance for larger screen sizes and another that was only visible for smaller screens. Both showed up regardless of screen size. Bootstrap classes like visible-xs and hidden-xs do not seem to affect the display of the Google Translate button.
You can set a global counter and make sure it's only called once.
<div id="google_translate_element"></div>
<script type="text/javascript">
var duplicate_google_translate_counter = 0;//this stops google adding button multiple times
function googleTranslateElementInit() {
if (duplicate_google_translate_counter == 0) {
new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
}
duplicate_google_translate_counter++;
}
</script>
<script type="text/javascript" src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
Had the same problem on RoR. Problem caused by cashing pages with turbolinks. I solved it with deprecating cashing all links in (when script loading it adds attr "data-turbolinks="false" to the body-tag)
Hello to all! I had the same issue and I KNOW is not the best practice but I fixed it with CSS just adding overflow: hidden and a right border on it.
It visually fix the problem until we get a solution and really saved time diving into JS files. Hope it works for you too. Cheers!

Resources