I have a page that gets the current location from the device's location services. I want to display this in a map, zoomed into the immediate area. I've tried doing this from OnParameterSetAsync, or OnAfterRenderAsync using ZoomToGeographicAsync, but neither works. The problem seems to be that my reference to the map component is null in these calls. I added a button that calls the same code when pressed and this works fine, but I'd like it to be done automatically. Short of adding a timer event to repeatedly check until it is safe to use the reference, how can I get this to work correctly?
I'm binding the reference like follows:
<GeographicMap @ref="@geoMap" Width="100%" Height="240px" Zoomable="true"> <GeographicSymbolSeries DataSource="locs" MarkerType="MarkerType.Triangle" LatitudeMemberPath="Lat" LongitudeMemberPath="Lon" MarkerBrush="Red" MarkerOutline="Black" /> </GeographicMap>
Hello Kevin,
I have been investigating into your requirement to zoom to an area on the GeographicMap component in Ignite UI for Blazor, and I would recommend continuing with the OnAfterRender method, but make a check to see if the “ref” object that you are passing your GeographicMap to is not null.
For example, our sample here is doing the following, where “MapRef” is the “ref” of the GeographicMap:
protected override void OnAfterRender(bool firstRender) { base.OnAfterRender(firstRender); if (MapRef != null && !firstRender) { var geoRegion = new Rect(-130, 15, new Size(65, 35)); MapRef.ZoomToGeographic(geoRegion); } }
Please let me know if you have any other questions or concerns on this matter.
Hi Andrew,
AfterRender doesn't help though - if you need to make the null check there then it's clearly because there's a chance the reference will be null. If it *is* null when AfterRender is called then the zoom won't be applied until the next render, which won't happen until the user does something to trigger it. I had seen the example you quoted, but tellingly it doesn't work in the sample either - when I view that sample the map remains unzoomed - when I look at the example the map remains fully zoomed out. I also note that there is a completely unexplained "await Task.Delay" call in the OnInitialised call, and can't help wondering if you're developers were aware of the issue and were trying things like that in order to attempt to work around it? Sadly, at the moment, it feels like this component isn't really fit for purpose and I'll need to look at wrapping a javascript component instead. What makes it worse is that the same component and the sample in your Angular release work perfectly
While it is true that the Angular IgrGeographicMap works a bit more reliably for this behavior, I believe we can attribute this to Angular exposing an override for ngAfterViewInit. This reliably happens at the point where the ViewChild is already referenced. In Blazor, the equivalent of this is essentially the OnAfterRender override for the page, but as you have noted, this can happen before the GeographicMap component is actually referenced into its property.
I have been experimenting with different ways of getting the map to zoom in on load, and the most reliable way I have found so far is actually to do something with the setter of the GeographicMap reference property that you define. Something like this appears to work reliably on my end where “GeoMap” is the “ref” of the GeographicMap object:
private GeographicMap _geoMap; public GeographicMap GeoMap { get { return _geoMap; } set { _geoMap = value; if (_geoMap != null) { Task.Delay(500).ContinueWith((t) => OnMapRender()); } } } public void OnMapRender() { this.GeoMap.ZoomToGeographic(new Rect(100, -40, new Size(50, 25))); StateHasChanged(); }
Thanks Andrew, that makes sense as I'd had that once or twice myself. I'd suspected it was something along those lines, but hadn't had a chance to investigate further yet
The Task.Delay was originally a way to prevent an InvalidOperationException in that the GeographicMap wasn’t “ready” from its base renderer. I have since found a better way, though, as I did not realize there was an EnsureReady method on the map. As such, I would recommend continuing with the setter of the GeoMapRef, but call this instead of the Task.Delay:
_geoMap.EnsureReady().ContinueWith((t) => OnMapRender());
I hope this helps! Please let me know if you have any other questions or concerns on this matter.
Why the Task.Delay, by the way? Is there a period after the reference is set when the component is not fully available? I notice the solution occasionally fails (no error, but no zoom applied), but I've not investigated why yet, so perhaps there's a timing related issue
Ah yes, nice way to work around it, better than the mess I came up with timer events for sure.