-1

Using the Visual Studio for Mac I created a solution with name TestBlazorWebAssemblyProgressiveNetCoreApp using the wizard and I selected the sample of WebAssembly of Blazor selecting "Hosted ASP.NET Core" and "Progressive web Application". Then I changed the NavMenu.razor with this code:

@foreach (Item item in m_Items)
{
    <div class="nav-item px-3">
        <NavLink class="nav-link" href="@item.Path" Match="NavLinkMatch.All">
            <span class="oi @item.TextStyle" aria-hidden="true"></span> @item.Title
        </NavLink>
    </div>
}

And I've added to this code to the @code section:

public class Item
{
    public ComponentBase Component { get; }
    public string Path { get; }
    public string Title { get; }
    public string TextStyle { get; }

    public Item(ComponentBase component, string path, string title, string textStyle)
    {
        Component = component;
        Path = path;
        Title = title;
        TextStyle = textStyle;
    }
}

private List<Item> m_Items = new List<Item> {
    new Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.Index(), "", "Home", "oi-home"),
    new Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.Counter(), "counter", "Counter", "oi-plus"),
};

public void AddItem(Item item)
{
    m_Items.Add(item);
}

Then in the MainLayout.razor, I added this code at the end:

@code {
    private NavMenu NavMenuItem {
        get
        {
            NavMenu navMenu = new NavMenu();
            //navMenu.AddItem(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Shared.NavMenu.Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.FetchData(), "fetchdata", "Fetch data", "oi-list-rich"));
            return navMenu;
        }
    }
}

and I modify the html code commenting the <NavMenu> and adding a call to @NavMenuItem property, like this:

@NavMenuItem
<!--<NavMenu />-->

When I use the instance the menu doesn't appear (but the code of property NavMenuItem is executed). And if I use the <NavMenu /> tag, the code works fine.

I would like to know what is wrong and if there are a way to add the instance of property to the razor page like a tag.

Thanks

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 2
    The main problem here is that you cannot create instances of components like this - they need to be created by the Blazor "renderer" because that also tracks their state. – Mister Magoo Apr 27 '23 at 23:20
  • OK. I suspected that I had created the component too late. Is there a way to create components before the Blazor “renderer” does its job? I would like to do something similar to the code commented on dynamically from the Database. – Asier Cayón Francisco Apr 28 '23 at 06:11
  • 1
    @NavMenuItem usually calls object's .ToString() method. invokes the builder to render a render fragment. You are going about this the wrong way. – maciek Apr 28 '23 at 08:30
  • 1
    You do not create components manually - if you want to render components dynamically, you should store the `Type` and any parameter data you need in your `Items`, then you can either use [DynamicComponent](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.dynamiccomponent?view=aspnetcore-7.0) or (in code) [RenderTreeBuilder.OpenComponent](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.rendering.rendertreebuilder.opencomponent?view=aspnetcore-7.0) to render them in the menu. – Mister Magoo Apr 28 '23 at 10:32

2 Answers2

1

@NavMenuItem usually calls object's .ToString() method. https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-7.0#expression-encoding

<NavMenu /> invokes the builder to render a render fragment. You are going about this the wrong way.

Check out the example: https://blazorrepl.telerik.com/wnkyQiEZ01lNWnBL48

Main.razor:

@example

<Example />

@code {
    Example example = new ();
}

Example.razor

<h1>You would have though this appears...</h1>

@code{
    public override string ToString()
    {
        return "Surprise!";
    }
}

The output:

Surprise!
You would have though this appears...
maciek
  • 724
  • 2
  • 4
  • 16
0

Finally, with your sugestion, I found the page of Blazor University and finaly I was be able to think up a solution to my problem. Sorry if I was a little lost with the philosofpy of Blazor/Razor, but today is the third day I was testing with this framework. I found a solution but with no instance al element in code. Instead of it I use an element with a property as parameter to pass an list of objects. Then in the object I add all elements injected in this parameter property into the property that use the razor NavMenu element to render the menu elements.

The code in the MainLayout is:

...
<NavMenu ExtraItems="ExtraItems" />
...
@code {
    private List<TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Shared.NavMenu.Item> ExtraItems { get; } = new List<TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Shared.NavMenu.Item>();
    protected override void OnInitialized()
    {
        TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Shared.NavMenu.Item item = new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Shared.NavMenu.Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.FetchData(), "fetchdata", "Fetch data", "oi-list-rich");
        ExtraItems.Add(item);
        base.OnInitialized();
    }
...
}

And in the NavMenu is:

...
<nav class="flex-column">
    @foreach (Item item in m_Items)
    {
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="@item.Path" Match="NavLinkMatch.All">
                <span class="oi @item.TextStyle" aria-hidden="true"></span> @item.Title
            </NavLink>
        </div>
    }
</nav>
...
@code {
    public class Item
    {
        public ComponentBase Component { get; }
        public string Path { get; }
        public string Title { get; }
        public string TextStyle { get; }
        
        public Item(ComponentBase component, string path, string title, string textStyle)
        {
            Component = component;
            Path = path;
            Title = title;
            TextStyle = textStyle;
        }
    }

    public IReadOnlyList<Item> m_ExtraItems;
    [Parameter]
    public IReadOnlyList<Item> ExtraItems
    {
        get => m_ExtraItems;
        set
        {
            m_ExtraItems = value;
            foreach(Item item in m_ExtraItems)
            {
                AddItem(item);
            }
        }
    }

    private List<Item> m_Items = new List<Item> {
        new Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.Index(), "", "Home", "oi-home"),
        new Item(new TestBlazorWebAssemblyProgressiveNetCoreApp.Client.Pages.Counter(), "counter", "Counter", "oi-plus"),
    };

    public void AddItem(Item item)
    {
        m_Items.Add(item);
    }
}

Thanks all.