โŒ

Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

My AuthorizeRouteView doesn't work when I use @attribute [Authorize] in Blazor and C#

My code is not loading the information when logged in. Only the navmenu worked.

@rendermode InteractiveServer

It's not directing me to the right place when I log in. My Blazor system needs to redirect when I log in.

The menu works but the body does not work correctly.

Router:

router

Navmenu:

navmenu

Program.cs:

program.cs

System login:

system login

System doesn't work:

system

I tried several things and I couldn't find a solution.

It's not directing me to the right place when I log in.

https://github.com/HoteldosNobres/HoteldosNobresBlazor/tree/Login

In MudAutoComplete Component, When the search results are more than 10, A button should display to add more items to the list

I am using MudAutoComplete component and Using MoreItemsTemplate to increment the count of MaxItems in the MudAutoComplete list. Here the count is increasing while hitting that buttton but UI with updated ItemCount is not rendering. My question is how to render UI upon change of item count?

I tried using StateHasChanged(); method to render the UI but it didn't work. I expect to render UI upon clicking on Add more items button which is in MoreItemsTemplate.

Here is the code:

<MudGrid>
    <MudItem xs="12" sm="6" md="4">
        <MudAutocomplete T="string" Label="US States" 
            @bind-Value="value1" 
            SearchFunc="@Search1"
            MaxItems = "@maxItems">
        <MoreItemsTemplate>
           <MudButton Color="Color.Primary" OnClick=AddItemCount>Add 10 more items </MudButton>
        </MoreItemsTemplate>
        </MudAutocomplete>
    </MudItem>
</MudGrid>

@code {
    private string value1;
    private int maxItems = 10;
    private string[] states =
    {
        "Alabama", "Alaska", "American Samoa", "Arizona",
        "Arkansas", "California", "Colorado", "Connecticut",
        "Delaware", "District of Columbia", "Federated States of Micronesia",
        "Florida", "Georgia", "Guam", "Hawaii", "Idaho",
        "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky",
        "Louisiana", "Maine", "Marshall Islands", "Maryland",
        "Massachusetts", "Michigan", "Minnesota", "Mississippi",
        "Missouri", "Montana", "Nebraska", "Nevada",
        "New Hampshire", "New Jersey", "New Mexico", "New York",
        "North Carolina", "North Dakota", "Northern Mariana Islands", "Ohio",
        "Oklahoma", "Oregon", "Palau", "Pennsylvania", "Puerto Rico",
        "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
        "Texas", "Utah", "Vermont", "Virgin Island", "Virginia",
        "Washington", "West Virginia", "Wisconsin", "Wyoming",
    };
 private async Task AddItemCount()
 {
     maxItems += 10;
 }
    private async Task<IEnumerable<string>> Search1(string value)
    {
        // In real life use an asynchronous function for fetching data from an api.
        await Task.Delay(5);

        // if text is null or empty, show complete list
        if (string.IsNullOrEmpty(value))
            return states;
        return states.Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase));
    }
}

Please run the above code here here

Blazor Server Application manage list item for multiple users

I am new to Blazor and have a basic question which I am not sure how to acheive this. I am building a POS Application in which users will have Touch application to sell products.

Now, I have all products loaded in a list below:

private List<ProductModel>? Products = [];

Which gets updated when user selects e.g. quantity increments when selected for sale and decrements when removed from sale. The good part is if I run 2 or 3 or 4 browser with same URL the quantity is shared across. Meaning you can run adjacent touch apps with shared list object and prevents from over selling.

However, I have search on the page as well which filters out the product that are displayed. Below is my code for Searching filter :

public async void OnSearch(string args)
{
    if (isChecked == true)
    {
        if (args != "")
        {
            // Filter products where VariationCombinations is null
            var productsToRemove = this.Products
                .Where(x => x.ProductSiteAuth.Any(y => y.VariationCombinations == null))
                .Where(e => (e.Name != null && !e.Name.ToUpper().Contains(args.ToUpper()) ||
                            (e.Short_Name != null && !e.Short_Name.ToUpper().Contains(args.ToUpper()))))
                .ToList();

            // Remove the filtered products from the original list
            foreach (var product in productsToRemove)
            {
                this.Products.Remove(product);
            }

            this.Products.ForEach(product =>
            {
                product.ProductSiteAuth?.RemoveAll(auth =>
                    auth.VariationCombinations != null &&
                    (
                        (auth.VariationCombinations.Product_Variation_Combination_Name != null && !auth.VariationCombinations.Product_Variation_Combination_Name.ToUpper().Contains(args.ToUpper())) ||
                        (auth.VariationCombinations.Product_Variation_Combination_Short_Name != null && !auth.VariationCombinations.Product_Variation_Combination_Short_Name.ToUpper().Contains(args.ToUpper()))
                    )
                );
            });
        }
        else
        {
            Products = await CacheService.GetDataAsync(CacheKeys.ProductsCache, ProductService.GetProductsList, new CacheOptions { SlidingExpiration = TimeSpan.FromMinutes(30) });
        }
    }
    else
    {
        if (args != "")
        {
            this.Categories = this.Categories.FindAll(e => (e.Title.Contains(args) || e.SubTitle.Contains(args) || e.Content.Contains(args) || e.Title.ToLower().Contains(args) || e.SubTitle.ToUpper().Contains(args.ToUpper()) || e.Content.ToUpper().Contains(args.ToUpper())));
        }
        else
        {
            this.Categories = CategoriesModel.GetCategories(this.FilteringValue).Result;
        }
    }
    StateHasChanged();
}

The issue I am facing in the code is that now since all browser tabs on same URL pointing to same shared product list, when 1 user on 1 tab searches it removes item from Product list and other users get issue when selecting that product, or if one 1 user searches product and the other user refreshes page they see filtered products as well.

How can I make search independent for each user where as share product list for quantity management ?

Please advise or at least guide me how can I do this ?

I want to share list for quantity management but for front end display it should not share products list.

Blazor webassembly component refreshes data only on initial page load

I have created blazor library that uses google maps and google places autocomplete for my project. I tied to implement autocomplete with map navigation to selected place. Now I am testing it with blazor webassembly and I am currently stuck with strange behaviour.

  1. Component updates data only when page is first time loaded.
  2. When page is navigated again, component doesn't respond to data change, even the change is triggered correctly.

Basically javascript function triggers method PlacesChanged

internal class GooglePlaceEventInfo
{
    private string _textBoxElementId;
    private Func<string, Task> _autocompleteInitializedCallback;
    private Func<GooglePlace, string, Task> _placeChangedCallback;

    public GooglePlaceEventInfo(string textBoxElementId, 
        Func<string, Task> autocompleteInitializedCallback, 
        Func<GooglePlace, string, Task> placeChangedCallback)
    {
        _textBoxElementId = textBoxElementId;
        _autocompleteInitializedCallback = autocompleteInitializedCallback;
        _placeChangedCallback = placeChangedCallback;
    }

    //Map evetns
    [JSInvokable("PlacesInitialized")]
    public async Task PlacesInitialized(string textBoxElementId)
    {
        if (_textBoxElementId != textBoxElementId)
        {
            throw new InvalidProgramException($"{nameof(PlacesInitialized)} method was called with invalid id container Div Id: {textBoxElementId}, expected Id is {_textBoxElementId}.");
        }

        if (_autocompleteInitializedCallback is not null)
        {
            await _autocompleteInitializedCallback.Invoke(textBoxElementId);
        }
    }

    [JSInvokable("PlacesChanged")]
    public async Task PlacesChanged(GooglePlace places, string textBoxElementId)
    {
        if (_textBoxElementId != textBoxElementId)
        {
            throw new InvalidProgramException($"{nameof(PlacesInitialized)} method was called with invalid id container Div Id: {textBoxElementId}, expected Id is {_textBoxElementId}.");
        }

        if (_placeChangedCallback is not null)
        {
            await _placeChangedCallback.Invoke(places, textBoxElementId);
        }
    }
}

Then there is a method in page where component is located. Two fields: _mapCenter, _zoomLevel are holding data that is used to trigger change in component.

private Task PlaceChanged(GooglePlace place, string textBoxElementId)
{
    if (place != null)
    {
        PlaceMarkerOnMap(place);
        var position = new GeolocationData(place.Geometry.Location.Lat, place.Geometry.Location.Lng);
        _mapCenter = position;
        _zoomLevel = 14;
        StateHasChanged();
    }
    return Task.CompletedTask;
}

Component:

                   <GoogleMap @ref="_googleMap"
                   Center="@_mapCenter"
                   Zoom="@_zoomLevel"
                   UsePlacesLibrary="true"
                   ApiKey="@_googleMapsApiKey" />

Everything is working fine on first page load. Means that map is set to desired position and the marker is placed. But navigation to different page and renavigation to map page breaks whole thing. PlacesChanged is still triggered but map component doesn't update itself. It just looks like StateHasChanged() does not have any effect anymore. Any help or thoughts would be appreciated. Thank you.

Edit: I wrote this part of my code wrongly. I was trying to attach proces to existing component, but instead I had to move the logic to separate component. After this everything is working perfectly. Thank you enet for pointing me in right direction. I also published this library on github if anybody is interested in such functionality.

Blazor cannot set style property

cannot set the style property of an element it's always empty. When I write out StyleString it says color: red

[Transposed verbatim from the comment]

AT(StyleString = ShouldBeError ? "color: red" : "") 
</div>
 @StyleString 
<MudCard> 
  <MudCardHeader> 
    <CardHeaderContent> 
      <MudText Typo="Typo.h6" Style="AT($"{StyleString}")"> 
       //..... 

ATcode { 
//... 
private bool ShouldBeError; 
private string StyleString = ""; 
private List<Location> Data = new List<Location>(); 

enter image description here

Blazor event isn't triggering. There must be an issue with my environment

I was having issues with .NET 8 Blazor Web App events and getting them to trigger. It started when my @bind-Value on my textarea wasn't populating my booking.Text property. But I did some experimenting and it turns out that none of my delegate event handlers are working.

I isolated the issue to a new project with just that in it - No change I restarted my PC - No change I reinstalled visual studio 2022 - No change I checked my setup, --version and --list-sdks: enter image description here

Here's the code I entered: enter image description here

I couldn't hit the breakpoint and the console didn't contain the string from the console.writeline()

enter image description here

I'm convinced there is something wrong with my setup somehow. I just don't know what. I just need ideas of things to try really. Thanks in advance!

What's the best way to get result from ReadAsStringAsync response?

What I have

This code calling my API:

HttpResponseMessage response = await Http.GetAsync("https://localhost:7094/api/MyResource/94d0e5ca-2be7-42f7-94dc-13955c11595c");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();

Console.WriteLine($"RESPONSE: {responseBody}");

var payload = JsonSerializer.Deserialize<MyClass>(responseBody);
var json = JsonSerializer.Serialize(payload);

Console.WriteLine($"PAYLOAD: {json}");

This code is called from OnInitializedAsync in a NET8 Blazor WASM client.

The response I get

The first WriteLine of the responseBody returns this:

{
    "creationOptions": 0,
    "id": 226,
    "isCanceled": false,
    "isCompleted": true,
    "isCompletedSuccessfully": true,
    "isFaulted": false,
    "result": {
        "content": {
            <Actual content>
        },
        "links": {
            <my hateoas links>
        }
    },
    "status": 5
}

That is correct.

What's failing

The deserialize of MyClass does not seem to happen correctly, because the second writeline returns this:

PAYLOAD: null

That's not unsurprising because the responseBody has added the http get results. I need to go down the json tree once.

I can't do:

var payload = JsonSerializer.Deserialize<MyClass>(responseBody.result);

Because responseBody is of type string, as you can see above.

I have also tried

to use

var payload = Http.GetFromJsonAsync<Payload<FabricageSoort>>("https://localhost:7094/api/MyResource/94d0e5ca-2be7-42f7-94dc-13955c11595c");

But that returns:

{ "content": null, "links": null }

I suspect that's a timing issue (data is not ready yet when I writeline).

What's the best way to get my actual payload from the json responseBody?

Blazor - How to dynamically load images from an sftp server

I've just started with Blazor and I want to try and make a store/portofolio kind of site where one of the pages is a gallery (similar to Instagram's profiles). I'm planning on using AWS S3 to store the photos, but I can't seem to get the photos to be loaded correctly.

I'm also using SSH.NET to connect to the S3 server.

The HTML portion

<body>
    <div id="feed" class="centered">
        @for (int l = 0; l < @GetLineCount(); l++) //each line has a max of 5 images
        {
            <div class="feed-line">
                @for (int c = 0; c < 5; c++)
                {
                    <img src="@imagesPaths[@l * 5 + @c]" /> 
                }
            </div>
        }
    </div>
</body>

And for the C# portion

@code {
    public List<string> imagesPaths = new();

    protected override void OnInitialized()
    {
        var privateKey = new PrivateKeyFile(@"PATH TO KEY");
        using SftpClient client = new(serverUri, 22, username, privateKey);
        client.Connect();

        if (client.IsConnected)
        {
            var list = client.ListDirectory("bucket-name/gallery/"); //Correctly retrieves a list with all the files to be loaded

            foreach (var sftpFile in list)
            {
                imagesPaths.Add(sftpFile.FullName); //This would then be retrieved on the html portion
            }
        }
        client.Disconnect();
    }
}

My current problem is knowing how to convert the SFTP image URI to a HTTP image URI.

  • either by downloading it to wwwroot or
  • somehow exposing the S3 uploaded HTTP URL with authentication (since just the URL will retrieve access denied)

Upgrading from Blazor 7 to Blazor 8 - OnAfterRenderAsync is not getting triggered

I am upgrading a Blazor 7.0 Server App to Blazor 8.0 Web App. I created a new project under the Blazor 8.0 Web App, using Interactive Server and copied my files there, per Microsoft's guidelines. Now, when executing my code, neither OnAfterRender nor OnAfterRenderAsync are getting triggered, which is needed for JS interop. I validated this with breakpoints at the start of the method.

Relevent parts of the Program.cs

builder.Services.AddRazorComponents().AddInteractiveServerComponents();

app.MapRazorComponents<App>().AddInteractiveServerRenderMode();

In App, I tried adding @rendermode="InteractiveServer" to the Routes as well, with no change.

I've searched here and on Google and have yet to find the answer. Even Microsoft's site isn't clear. From what I've seen through learn.microsoft.com, it looks like I should be working.

Blazor 8: Server side rendering with interactive layout

Blazor .NET 8 introduces the new SSR for razor pages of "static" character. This is nice for login, status page or other non-interactive pages. On the downside it seems that having pure SSR pages forces the layouts to be non-interactive as well. In other words, I cannot have C# frontend code (interaction) on the layouts (MainLayout.razor etc). If I want that, I neeed to move both the MainLayout.razor AND the Routes.razor to the Client project and set their rendermode to InteractiveAuto. However, then I cannot render pure SSR pages any more, since routing is taken over completely by the client logic (Blazor Server/WASM)

Is there a way to combine the best of both worlds? I'd like to have a few static pages rendered only by SSR, while most of the other pages require an interactive layout with widgets etc.

Essentially the Router should always be rendered server-side, while the layout's rendermode should depend on the scenario:

  • Server-side layout for static pages
  • interactive client-side layout for interactive pages

Is this possible?

Variable display issue in Razor page in Blazor App

I'm learning Blazor. On my page the contents of the _persons list just flashes by when I click the Search button, is this variable being garbage collected? Thanks in advance.

@page "/"
@using BlazorPorter.Models
@using BlazorPorter.Context
@using Microsoft.EntityFrameworkCore
@inject SqliteContext SqliteContext
@rendermode InteractiveServer

<PageTitle>Home</PageTitle>

<form class="form-control">
    <input placeholder="id" @bind="_id"/>
    <input placeholder="name" @bind="_name"/>
    <button class="btn-primary" @onclick="@Insert">Insert</button>
</form>

<form class="form-control">
    <button class="btn-primary" @onclick="Search">Search</button>
</form>
@if (_persons != null)
{
    @foreach (Person p in _persons)
    {
        <p>@p.Id</p>
        <p>@p.Name</p>
    }
}

@code
{
    private string? _id;
    private string? _name;
    private List<Person>? _persons;

    private async Task Insert()
    {
        if (_id != null && _name != null)
        {
            await SqliteContext.AddAsync(new Person() { Id = _id, Name = _name });
            await SqliteContext.SaveChangesAsync();
        }
    }

    private void Search()
    {
        if (SqliteContext.Persons != null)
        {
            _persons = SqliteContext.Persons.ToList();
        }
    }
}

At first I thought the SqliteContext wasn't querying the content because it wasn't showing up on the page. But after I debugged it using breakpoints, I realized it was there. On successive clicks of the Search button, the content of the _persons list also flashes.

How to enable compressed binary messaging in SignalR with Blazor WebAssembly with MessagePack

Hello fabulous Blazor & SignalR gurus!

My objective is to add (hopefully compressed) binary messaging between my ASP.NET Core server with SignalR and my Blazor WebAssembly by properly configuring 'MessagePack'

Messaging is fully operational in both directions but I notice in the browser's network monitor that the websocket used by SignalR still sends its messages via JSON and Base64. Not only is my stream not compressed as configured server-side but Base64 throws away 2 bits out of 8 yielding a pipe efficiency decrease of around 28%.

It is my understanding that the 'MessagePack' protocol be used to enable full binary communication (removing the usage of JSON / Base64) and I have been following the relevant article at https://learn.microsoft.com/en-us/aspnet/core/signalr/messagepackhubprotocol

As recommended in the above instructions, here is my current code in my server-side WebApplication builder:

  .AddMessagePackProtocol(options => {
    options.SerializerOptions = MessagePackSerializerOptions.Standard
      .WithResolver(MessagePack.Resolvers.StandardResolver.Instance)
      .WithSecurity(MessagePackSecurity.UntrustedData)
      .WithCompression(MessagePackCompression.Lz4Block)
      .WithCompressionMinLength(256);
    });

However, the same instructions mention to add the following to my '.net client'...

    var hubConnection = new HubConnectionBuilder()
                        .WithUrl("/chathub")
                        .AddMessagePackProtocol()
                        .Build();

Unfortunately, the sample I've been basing my WebAssembly work on has no concept of a HubConnectionBuilder object and creates its builder as follows:

var builder = WebAssemblyHostBuilder.CreateDefault(args);

Trying everything I could think of, I cannot find a way to find a sub-object of 'WebAssemblyHostBuilder' where I could call the 'AddMessagePackProtocol()' so that MessagePack also works client-side

EDIT: I was looking at the wrong place! Client-side SignalR init is in .razor page (see answer post below)

My hunch is that the server side is properly configured for (compressed) MessagePack but the web-assembly client is not, so I still get JSON + Base64 messaging. (I can live without compression support within MessagePack but would like to lose the 28% pipe efficiency drop caused by JSON + Base64)

Key questions:

  • Q1: Is MessagePack supported in Blazor WebAssemblies?
  • Q2: What is the proper procedure to configure the SignalR pipe within Blazor WASM to use MessagePack?
  • Q3: Is MessagePack the highest-performance pipe for my client/server architecture?

Thank you very much for any hint you can offer!

Jean-Pierre

How to navigate posting form data to external url in Blazor Server Side?

I'm working on an e-commerce which has to redirect to an external payment gateway while posting form data.
A trivial process in Html5 but I wonder how may we manage this on a blazored way.

<form action="https://externalUrl.com" method="post">
  <input type="number" name="amount">
  <input type="submit" value="Submit">
</form>

blazor interface not updating correctly when removing item from List<>

I have a child component that is being generated inside a foreach loop for List<> of models. That child component has a delete button on it and when clicked, it sends a message to the parent and the model for that component is removed. The interface should update with that component no longer being visible on the view. The interface is updating and removing a child component from the view, but unless it's the last item in the list, it's not removing the correct one (but only on the view). If I click another button on the page, the interface updates correctly with the removed view gone. I have added a call to StateHasChanged() right after the item is removed from the list. I have also put a breakpoint in the method that is removing the item. The model being passed in is correct and when inspecting the list after the item is removed, it is also correct.

Is there some other way I can force the interface to update after I remove this item from the list? As I said above, when I click another button or perform any other action on the page, the children components update correctly. I have tried using System.Reactive in an observable to pass the message to the parent from the child as well as Invoking an EventCallback from the child to pass the message to the parent, same behavior with both.

here is the relevant code on the parent:

    _removeDogSubscription = DogCommunicationService.RemoveDogAgent.Subscribe(dog =>
    {
        DeleteDog(dog);         
    });

private void DeleteDog(DogModel dog)
{
    Dogs.Remove(dog);
    StateHasChanged();
}

Cache Busting for Static Files in Blazor 8 Across Different Render Modes

I'm working on a Blazor 8 application and I'm having trouble with browser caching of my static files. When I make changes to the CSS files, the changes are not immediately visible because the browser serves the old version of the file from cache.

In ASP.NET Core, I would use the asp-append-version tag helper to append a version query string to the URL of the CSS or JS file. This version query string changes whenever the file changes, forcing the browser to download the new version of the file instead of serving the old one from cache.

In Blazor and .NET 8, the general page format is defined in the App.razor file. Here's what my App.razor file looks like:

<!DOCTYPE html>
<html lang="fa" dir="rtl">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <link rel="stylesheet" href="fonts/font.css">
    <link rel="stylesheet" href="css/bargeh.css">
    <link rel="stylesheet" href="css/header.css">
    <link rel="stylesheet" href="css/footer.css">
    <script src="js/jquery-3.6.0.min.js"></script>
    <script src="js/header.js" defer></script>
    <script src="js/bargeh.js" defer></script>
    <HeadOutlet />
</head>

<body>
    <Routes />
@* ReSharper disable Html.PathError *@
<script src="_framework/blazor.web.js"></script>
@* ReSharper restore Html.PathError *@
</body>

</html>

As you can see, I'm referencing several CSS and JS files in the head section of the HTML document. I tried manually appending a version query string to the URLs of these files, like this:

<link rel="stylesheet" href="css/bargeh.css?v=1.0.0">

But this approach requires me to manually increment the version number each time I make changes to the CSS file, which is not ideal.

Moreover, my application might use server-side rendering (SSR), server, WebAssembly (WASM), or auto render mode. So, I need a workaround that works for all of these.

Is there a way to automatically append a version query string to the URLs of CSS files in the App.razor file of a Blazor.NET 8 application, similar to how asp-append-version works in ASP.NET? Any help would be greatly appreciated. Thank you!

โŒ
โŒ