Why can't i add data to google maps in a blazor component without running code twice? - google-maps-api-3

I have no previous Blazor and very little asynchronous programming experience. I'm exploring using Blazor for a google maps project and trying to add a geojson point to the map using the google maps javascript api. I wasn't sure of the best way to do this, so my plan was to update the inner html of a hidden div in a blazor component and reference that value in my google map api call.
When I call AddMapData (which gets a geojson string from my db) to set the html of the inner div in both OnInitializedAsync and OnAfterRenderAsync everything works great:
#page "/map"
#inject IJSRuntime JSRuntime
#using DataAccessLibrary
#inject IGeoData _db
<div id="info-box"></div>
<div hidden id="mapData">#mapData </div>
<h3>Map</h3>
<div id="map" ></div>
#code {
private string mapData;
private List<string> data;
async Task<bool> AddMapData()
{
data = await _db.GetGeoData();
mapData = data.First<string>();
mapData = mapData.Substring(1, mapData.Length - 2);
return true;
}
protected override async Task OnInitializedAsync()
{
await AddMapData();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await AddMapData();
await JSRuntime.InvokeVoidAsync("initMap", null);
}
}
}
Why do I have to call AddMapData in both? My first thought was to only call it in the OnAfterRenderAsync override, but the div.innerhtml value is "". Any thoughts or input is appreciated.

I fixed this by adding StateHasChanged() after AddMapData() in OnAfterRenderAsync. I no longer override OnInitializedAsync.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await AddMapData();
StateHasChanged();
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("initMap", null);
}
}

Related

Unity async/await and IO operations with Firestore (Firebase)

I've developed a Unity App that uses Firebase as a BaaS and Firestore as a Database.
Firebase has a Client SDK to make calls that are usually called from client to server by an URL endpoint.
My concern is how my methods should be implemented to correctly work on client without blocking the user experience, cause if I made a heavy request, my Unity App is blocked, and no interaction is allowed to the user.
This is the code of my client DatabaseManager with the methods to retrieve a User from Firestore:
public class DatabaseManager
{
public DatabaseManager(FirebaseFirestore db)
{
this.db = db;
}
public async Task<User> GetUserByUIDAsync(string uid)
{
string documentID = uid;
return await AsyncGetDocumentFromCollection<User, User_FirestoreData>(COL_ID_USERS, documentID);
}
public async Task<PlainData> AsyncGetDocumentFromCollection<PlainData, FirestoreData>(string collectionID, string documentID) where PlainData : IConvertToFirestore<FirestoreData> where FirestoreData : IConvertToPlainData<PlainData>
{
try
{
DocumentReference docRef = db.Collection(collectionID).Document(documentID);
DocumentSnapshot documentSnapshot = await docRef.GetSnapshotAsync();
if (documentSnapshot.Exists)
{
Debug.Log("Get Document data for document:" + documentSnapshot.Id);
FirestoreData firestoreData = documentSnapshot.ConvertTo<FirestoreData>();
return firestoreData.ToPlainData();
}
else
{
Debug.Log($"Document {documentSnapshot.Id} does not exist!");
}
}
catch (Exception e)
{
Debug.Log(e);
}
return default(PlainData);
}
}
This is a simple call and when it's called from any MonoBehaviouryou couldn't notice the load difference when you call it like:
using UnityEngine.UI;
public class MyMono : MonoBehaviour
{
private void DatabaseManager db;
[SerializedField] private Button button = null;
private void Awake()
{
button.onClick.AddListener(async ()=> await CustomAwakeAsync(db));
}
private async Task CustomAwakeAsync(DatabaseManager db)
{
//if this Async method is heavier, this will block the main UI thread when the button is pressed
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
}
But if instead of GetUserByUIDAsync I make a heavy call, or multiple recursive calls my application UI will freeze until it's finished...which is bad.
How should I build my code to avoid these case?
Note:
My easy way to test if it's blocking UI thread is having this class attached to a GameObject with Image component:
using UnityEngine;
public class InfiniteRotate : MonoBehaviour
{
public float speed = 1;
// Update is called once per frame
private void Update()
{
this.gameObject.transform.Rotate(0, 0, 1 * Time.deltaTime * speed);
}
}
If the image stop spinning, means that async/await is blocking the UI thread.
Your code as shown:
private void CustomAwake(DatabaseManager db)
{
await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...should be producing the following error:
error CS4033: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Even if somehow you managed to silence this error the method signature private void CustomAwake(DatabaseManager db) clearly indicates that this method is synchronous to the caller.
OP:
But if instead of GetUserByUIDAsync I make a heavy call, my application UI will freeze until it's finished...which is bad.
So if you are calling this from the same thread as Update, FixedUpdate etc (which by the looks of it you are) then you are going to block Unity and thus slow down your game.
If you are going to use async/await then you need to do so all the way back to the original caller.
Make it asynchronous
Change the method to:
private async Task<User> CustomAwake(DatabaseManager db) // Note the async Task
{
return await db.GetUserByUIDAsync("xdfipñfajrfiñar");
}
...and ensure that whatever calls it uses await in order to get the User.

Child OnInitializedAsync called before parent OnInitializedAsync finishes

I've got MainLayout.razor
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
var authToken = await _localStorage.GetItemAsync<string>("authToken");
ApiClient.SetAuthorization(authToken);
Console.WriteLine($"User {user.Identity.Name} is authenticated. {authToken}");
}
else
{
NavigationManager.NavigateTo("Login");
Console.WriteLine("User is NOT authenticated.");
}
}
And site Index.razor
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
this.Data = await ApiClient.GetData();
}
Unfortunately ApiClient.GetData is run first before I even set the Bearer token. How can I fix it?
A quick answer to this.
Do not load the parent control in async, rather:
protected override void OnInitialized()
And you can also place the child load in this form:
protected override async Task OnAfterRenderAsync(bool firstRender)
I believe I also had a cleaner solution for this a while ago, but I will need to dig through my code to find it for you.

Why is my Blazor Server App waiting to render until data has been retrieved, even when using async?

I'm building a Blazor Server app using .NET 6.
As an example I have a list of customers that I retrieve from a Sqlite db using Entityframework Core. I want to show a loading indicator when the data is still being retrieved. Like so:
#page "/customers"
#inject CustomerService CustomerService
#if(customers.Count == 0)
{
<p>Loading...</p>
}
else
{
<table>
#foreach(Customer customer in customers)
{
<tr>
<td>
#customer.Id
</td>
<td>
#customer.Name
</td>
</tr>
}
</table>
}
When I use following data implementation it works fine:
private List<Customer> customers = new();
protected async override Task OnInitializedAsync()
{
await Task.Run(() =>
{
this.customers = CustomerService.GetCustomers();
});
}
However when I use following data implementation it doesn't work. Meaning that the entire page only becomes visible once the data has been loaded (white page while waiting).
private List<Customer> customers = new();
protected async override Task OnInitializedAsync()
{
this.customers = await CustomerService.GetCustomersAsync();
}
CustomerService.cs
public class CustomerService
{
private CustomerContext _customerContext;
public CustomerService(CustomerContext customerContext)
{
_customerContext = customerContext;
}
public async Task<List<Customer>> GetCustomersAsync()
{
return await _customerContext.Customers.ToListAsync();
}
public List<Customer> GetCustomers()
{
return _customerContext.Customers.ToList();
}
}
I would expect my second implementation to also work asynchronously. Could anyone help me understand please? What am I missing here? Thanks in advance for the help!
It depends on your database whether ToListAsync() is really and always async. It may execute synchronously, depending on the provider, caching etc.
There is a simple fix:
private List<Customer> customers = new();
protected async override Task OnInitializedAsync()
{
await Task.Delay(1);
this.customers = await CustomerService.GetCustomersAsync();
}
The async Delay() will allow the loading... message to be displayed.
Note that you ought to call GetCustomers() without the Task.Run() though.
... from a Sqlite db
Here is an answer about sqlite.. It does not actually support async operations. The EF provider just pretends.

Make a network call from code behind's constructor Xamarin forms

I am making a network call inside code behind constructor. I have to do this because I am passing parameter to constructor, then using those initiate the network call. Because of I am doing this inside the constructor I have to use Task.Run option which is not a good solution. I tried factory method also but I am unable to use it since passing parameters are done by click event. Can anyone suggest me a better solution. Thanks
Class Home{
private async void HomeLandingListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var item = (HomeLanding)e.Item;
await Navigation.PushAsync(new PolicyDetailsMotor(item.id, p_end_date));
}
}
Network call class
public partial class PolicyDetailsMotor : ContentPage
{
public PolicyDetailsMotor(string id, string p_end_date)
{
InitializeComponent();
this.id = id;
this.p_end_date = p_end_date;
Title = "Motor Policy Details";
Task.Run(async () =>
{
var result = await api_class.getMotorPolicyDetails(id, p_end_date);
});
}
}

xamarin.forms authorization without 3rd party libraries

I'm trying to make authorization in xamarin forms and i dont know how to structure it. I'm using MVVM and for my authorization i wanna use JWT . I want to check if the token is valid then go to certain page . when i put the validation code inside the onappearing method the page is still visible for a very small amount of time and when i put it inside the constructor of the page the navigation doesn't work. How should this authorization should be done?(should i make another transition page with something like an activity indicator ?)
this is the code i use for the token validation
public async Task CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
}
}
when i put it inside the constructor of the page the navigation
doesn't work.
You can call the push service like this in Main thread:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new myViewModel();
CheckIfUserIsLoggedIn();
}
public async void CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
Device.BeginInvokeOnMainThread(async () => {
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
});
}
}
}

Resources