rungwiroon / BlazorGoogleMaps

Blazor interop for GoogleMap library
MIT License
319 stars 102 forks source link

Using BlazorGoogleMaps inside a component binding lat long to a parameter #276

Closed gasand closed 12 months ago

gasand commented 1 year ago

Hi guys, I'm pretty new to blazor so maybe this question is trivial, but still I hope you can help me. I'm using your BlazorGoogleMaps inside a component that renders the detail of a record. In the record I have lat and long. Like this:

[Parameter] public MyRecordObj TheObj { get; set; }

I wonder if there is a way to bind lat and long of the record to a marker in a map. I have all my records in a grid, with a simple button that opens the detail in the same page.

Right now I'm just able to open the detail, show the marker but the location of the marker is always referring to the first record I open. Prehaps this is because I'm using the OnInitialized and the OnAfterMapInit events that are triggered just the first time I open my component.

Do you have an idea on how I could get around this issue? Many thanks in advance.

valentasm1 commented 1 year ago

Do you have code how you create marker? Maybe store added markers referencies and when click button remove old add new.

gasand commented 1 year ago

Hi @valentasm1 thanks for your feedback. Down you have a simplified version of my code. The "problem" is that the click on the button is in the page that uses my component. I think this compnent should not be aware of any click, my idea is that it's role is to just show farmer data. Also is not just about the marker it is also about the location of the center of the map because one farmer can be in Italy and another in Japan... So I was searching for something similar to the @bind-Text="@TheFarmer.CompanyName" that I use for the textbox, let's say @bind-Lat=....

    @inherits BaseComponentBo;
    @using GoogleMapsComponents
    @using GoogleMapsComponents.Maps
    @using GoogleMapsComponents.Maps.Places

   <div class="row">

        <div class="col-md-4">
            <span>Company Name</span>
                <TextEdit @bind-Text="@TheFarmer.CompanyName" />

        </div>
    </div>
    <div class="row">

        <div class="col-md-3" style="height:400px">
            <GoogleMap @ref="@(this.map1)" Id="map1" Options="@(this.mapOptions)" OnAfterInit="async () => await OnAfterMapInit()"></GoogleMap>
        </div>

    </div>

  @code {

    [Parameter]
    public Farmer TheFarmer { get; set; }

    private GoogleMap map1;
    private MapOptions mapOptions;

    protected override void OnInitialized()
    {
        mapOptions = new MapOptions()
            {
                Zoom = 13,
                Center = new LatLngLiteral()
                {
                    Lat = TheFarmer.GeoCoordinate.Latitude,
                    Lng = TheFarmer.GeoCoordinate.Longitude
                },
                MapTypeId = MapTypeId.Roadmap
            };

    }

    private async Task OnAfterMapInit()
    {
        var position = await map1.InteropObject.GetCenter();

         var marker = await Marker.CreateAsync(map1.JsRuntime, new MarkerOptions()
             {
                 Position = position,
                 Map = map1.InteropObject,
                Title = TheFarmer.CompanyName,
             });
    }

  }
valentasm1 commented 1 year ago

Why you could not do? Position = new LatLngLiteral() {Lat = TheFarmer.GeoCoordinate.Latitude, Lng = TheFarmer.GeoCoordinate.Longitude },

gasand commented 12 months ago

I see... thank youm Now, I did it like that but this des not change the fact that once I click on the second farmer I keep seenig the map of the first. Here you have the code of the page that uses my component My component is and it's code is the code I sent erlier. (I Use Blazorise for the )

    @page "/farmerstest"
    @inherits BaseComponent;
    @attribute [RenderModeServer]
    @attribute [StreamRendering(true)]

    @if (farmers == null)
    {
        <p><em>Loading...</em></p>
    }
    else
    {
        <QuickGrid Class="table table-bordered" Items="@farmers" @ref="grid">
            <TemplateColumn Title="" Class="actioncolumn">
                <Buttons>
                    <Button Class="text-nowrap" Color="Color.Primary" Outline Size="Size.Small" Clicked="@(() => Edit(context))">
                         <i class="bi bi-pencil"></i> Edit
                     </Button>

         </Buttons>
     </TemplateColumn>

     <PropertyColumn Property="@(p => p.Id)" Title="n." Class="idcolumn" Sortable="true" />
     <PropertyColumn Property="@(p => p.CompanyName)" Sortable="true" Title='Company Name' />

 </QuickGrid>

}

@if (currentFarmer != null)
{
    <Modal @bind-Visible="@modalVisible">
        <ModalContent Centered Size="ModalSize.Fullscreen">
             <ModalHeader>
                 <ModalTitle Size="HeadingSize.Is6">@currentFarmer.CompanyName</ModalTitle>
                 <CloseButton />
             </ModalHeader>
             <ModalBody>
                 <FarmerDetailSimple TheFarmer="@currentFarmer" @ref="farmerDetail"></FarmerDetailSimple>
             </ModalBody>
             <ModalFooter>
                 <Button Color="Color.Secondary" Clicked="@HideModal">Close</Button>
             </ModalFooter>
         </ModalContent>
     </Modal>

}
@code {

QuickGrid<Farmer>? grid;
Farmer? currentFarmer;
FarmerDetailSimple farmerDetail;
private bool modalVisible;
private IQueryable<Farmer>? farmers;

protected override async Task OnInitializedAsync()
{
    LoadFarmersData();
}

protected async void LoadFarmersData()
{
    List<Farmer> res = new List<Farmer>();
    Farmer f1 = new Farmer();
    f1.CompanyName = "F1 Rome";
    f1.GeoCoordinate = new GeoCoordinate();
    f1.GeoCoordinate.Latitude = 41.901776772336824;
    f1.GeoCoordinate.Longitude= 12.493411787992772;
    res.Add(f1);

    Farmer f2 = new Farmer();
    f2.CompanyName = "F2 Tokyo";
    f2.GeoCoordinate = new GeoCoordinate();
    f2.GeoCoordinate.Latitude = 35.958677762740685;
    f2.GeoCoordinate.Longitude = 139.75367987882356;
    res.Add(f2);

    farmers = res.AsQueryable();

}

private Task ShowModal()
{
    modalVisible = true;
    return Task.CompletedTask;
}

private Task HideModal()
{
    modalVisible = false;
    return Task.CompletedTask;
}

private Task Edit(Farmer p)
{
    currentFarmer = p;

    ShowModal();
    return Task.CompletedTask;
}

}

valentasm1 commented 12 months ago

Please dont take it personally, but issues are for bugs/improvements for map. If you need generic help you could try stack overflow or other dedicated websites. You could try clone solution and start server side demos. There are many great examples of markers. Maybe it would be enought just to dig into it without cloning https://github.com/rungwiroon/BlazorGoogleMaps/tree/master/ServerSideDemo/Pages

gasand commented 12 months ago

Not taking it personally and I've already checked all of the samples, I Just think this compenent doesn't allow to "bind" lat long center of the map and/or lat long of a marker to a property of one object. I'was expecting something like @bind-Lat and @bind-Long, wich I cannot find in your examples.

The cool part of blazor is binding that's why I think your component is very cool but in this area can be inproved.

valentasm1 commented 12 months ago

Be be able to bind props it should be EventCallback for it. Please provide full solution in github. I think you a bit missunderstood how this library works.

Nickztar commented 11 months ago

@gasand It could be possible to make markers (and other parts) bindable but it would require a lot of changes to the library. If you want to try make something like that (doesn't have to be part of the library) you could create a component which abstracts the calls to BlazorGoogleMaps and calls .SetOptions when the parameters of the component changes. Having this built in to the library would be nice but don't think anyone currently has time to make those changes. I could help guide you if you want to submit a PR och just make the component.

gasand commented 11 months ago

@Nickztar cool! Thank you! You mean design a "hostComponent" for the GoogleMap component that is able to mimic the binding. I understand the approach.... could you please post a mockup of the "hostComponent" you have in mind? I'll be happy to try it and let you know!