2

I want to show/hide the map on button click action. I tried to achieve this by setting a bool variable which I set/reset in the on click action and check it in the razor page like in the example below:

        [Inject]
        public IJSRuntime JSRuntime { get; set; }

        protected bool displayMap = false;

        protected async Task OpenMap()
        {
            displayMap = !displayMap;
            if (displayMap)
            {
                await JSRuntime.InvokeVoidAsync("initialize", null);
            }
        }
            @if (displayMap)
            { 
                <MudCard>
                    <MudCardContent>
                        <div id="map" style="height:500px;width:100%;">
                        </div>
                    </MudCardContent>
                </MudCard>
            }

But seems like this solution won't work, because I get a rendering error:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Map: Expected mapDiv of type Element but was passed null. Error

Seems like the initialize function is called before the div is rendered, but I don't know how to fix this problem. Please help!

H H
  • 263,252
  • 30
  • 330
  • 514
Ion
  • 81
  • 1
  • 9
  • Welcome to Stack Overflow. Please read about [asking questions](https://stackoverflow.com/help/asking). Images of code are not searchable, can not be copied by someone working on the problem, and are not accessible. Please [edit] the question to replace or augment the image with the text that it contains. – Jason Aller Aug 30 '21 at 14:37
  • @JasonAller thank you! I've fixed the problem. – Ion Aug 30 '21 at 14:51
  • @Ion, you'll need to instantiate your object ( a Google map) from the OnAfterRender{Async} pair, only once, after your current component has been initialized, and all its children have been rendered. You can then display or hide the Google map, depending on the value of the displayMap variable. See here (https://stackoverflow.com/a/64572906/6152891), how I do the same thing, but using Leaflet map. The principal is the same. Again, you must instantiate your JS object only after the current component has been rendered – enet Aug 30 '21 at 18:12
  • It depends on how you want to manage the map state. If the map is shown infrequently you can save on the initialization cost. – H H Aug 30 '21 at 18:22
  • How often is the Map used? – H H Aug 30 '21 at 19:11
  • After Show/Hide/Show, do you want to maintain state (position, zoom) from the first Show? – H H Aug 30 '21 at 19:12
  • @HenkHolterman it's one of the main features, so it should used very often – Ion Aug 30 '21 at 19:14
  • @HenkHolterman no, just show and hide – Ion Aug 30 '21 at 19:20
  • If "no" means you want to reset the map then use my first answer. – H H Aug 30 '21 at 19:23

1 Answers1

1

When you want to maintain map state between show/hide then instead of

@if (displayMap)
{ 
    <MudCard>
        <MudCardContent>
            <div id="map" style="height:500px;width:100%;">
            </div>
        </MudCardContent>
    </MudCard>
}

You can use

<MudCard hidden="@(!displayMap)">
    <MudCardContent>
        <div id="map" style="height:500px;width:100%;">
        </div>
    </MudCardContent>
</MudCard>

in combination with

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await JSRuntime.InvokeVoidAsync("initialize", null);
    }
}

and remove the await JSRuntime.InvokeVoidAsync(...) line from the button click.

H H
  • 263,252
  • 30
  • 330
  • 514
  • This is the solution. I tought that using the if statement is the same as hide. Here was my mistake. Thank you very much for your answer! – Ion Aug 30 '21 at 19:24
  • 1
    This is actually very important to understand. Conditional logic actual doesn't render elements at all. Your component might have 2000 lines, but the client could have just 4 or 5. – Bennyboy1973 Aug 30 '21 at 21:02