Best practice to work with Hidden property in menutree (Version 18.3)

In version 18.3  the ShowItems and HideItems methods have been depreciated. Along with that, the component type safety was improved and a Hidden attribute/property was added to the MenuItem type. https://blazor.syncfusion.com/documentation/release-notes/18.3.35/?type=all#menu

But there does not seem to be supporting methods to work with the Hidden attribute so it becomes very clumsy to work with. The best I could do was to use the new GetItemIndex which returns a list of index values to the sought menuitem. Example:

        var index = Menu.GetItemIndex(new MenuItem { Text = "Aggregates" });  // on second level menu so two ints are returned in the list.
        MainMenuItems[index[0]].Items[index[1]].Hidden = true;  // Using the indices to set the Hidden property.


This component should have a method to find the menuitem and set the hidden property. In the meantime, I'll extend the component as a workaround but I should not have to.

Mike

10 Replies

MK Mohan Kumar Ramasamy Syncfusion Team October 6, 2020 08:54 AM UTC

Hi Mike, 
 
Sorry for the inconvenience caused. In our latest version (v18.3.35), we have included some breaking changes in Menu Bar component. We have provided tag directive support for SF Menu component. You can disable and hidden the menu item in tag level. Please refer the below code snippets. 
 
 
@using Syncfusion.Blazor.Navigations 
@using Syncfusion.Blazor.Buttons 
 
<SfMenu TValue="MenuItem"> 
    <MenuItems> 
        <MenuItem Text="File" IconCss="em-icons e-file"> 
            <MenuItems> 
                <MenuItem Text="Open" IconCss="em-icons e-open"></MenuItem> 
                <MenuItem Text="Save" IconCss="em-icons e-save" Disabled="false"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Exit" Hidden="@isHidden"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
 
        <MenuItem Text="Edit" IconCss="em-icons e-edit" Hidden="@isHidden"> 
            <MenuItems> 
                <MenuItem Text="Cut" IconCss="em-icons e-cut"></MenuItem> 
                <MenuItem Text="Copy" IconCss="em-icons e-copy"></MenuItem> 
                <MenuItem Text="Paste"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
 
        <MenuItem Text="View"> 
            <MenuItems> 
                <MenuItem Text="Toolbars"> 
                    <MenuItems> 
                        <MenuItem Text="Menu Bar"></MenuItem> 
                        <MenuItem Text="Bookmarks Toolbar"></MenuItem> 
                        <MenuItem Text="Customize"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Zoom"> 
                    <MenuItems> 
                        <MenuItem Text="Zoom In"></MenuItem> 
                        <MenuItem Text="Zoom Out"></MenuItem> 
                        <MenuItem Text="Reset"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Full Screen"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Tools"> 
            <MenuItems> 
                <MenuItem Text="Spelling & Grammar"></MenuItem> 
                <MenuItem Text="Customize"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Options"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Help"></MenuItem> 
    </MenuItems> 
</SfMenu> 
<SfButton Content="Hide" @onclick="Click"></SfButton> 
 
@code { 
    private bool isHidden = false; 
 
    private void Click() 
    { 
 
        isHidden = true; 
 
    } 
} 
 
 
 
For your reference we have prepared a sample based on this, please refer below link. 
 
 
We would like you to review the breaking changes from the below location before you upgrade. 
  
Please, refer the following links to know more about SF Blazor Menu Bar component    
   
 
Please let us know, if you need any further assistance. 
 
Regards, 
Mohan kumar R 



MC Mike Chafin October 6, 2020 11:04 AM UTC

I appreciate the answer but it was not an answer to the question I asked.

I need to change the "Hidden" attribute via code without the use of a variable but rather directly. Take the scenario where the signed-in user in an authorized role and based upon that role menu access is shown but in another role, it is not allowed. The access for the roles can overlap thus using a variable does not work.

What I really want is a supporting method to work with the Hidden attribute so it becomes very clumsy to work with. The best I could do was to use the new GetItemIndex which returns a list of index values to the sought menuitem. Example:

        var index = Menu.GetItemIndex(new MenuItem { Text = "Aggregates" });  // on second level menu so two ints are returned in the list.
        MainMenuItems[index[0]].Items[index[1]].Hidden = true;  // Using the indices to set the Hidden property.


This component should have a method to find the menuitem and set the hidden property. In the meantime, I'll extend the component as a workaround but I should not have to.




MK Mohan Kumar Ramasamy Syncfusion Team October 7, 2020 05:03 PM UTC

Hi Mike, 
 
We have checked your reported query, we can achieve your requirement using OnItemRender event. Please refer the below code snippets. 
 
 
<SfMenu Items="@menuItems"> 
            <MenuEvents OnItemRender="ItemRender" TValue="MenuItem"></MenuEvents> 
        </SfMenu> 
@code{ 
 
    private string[] hiddenItems = new string[] { "Open", "Zoom In" }; 
    private void ItemRender(MenuEventArgs<MenuItem> args) 
    { 
        if (hiddenItems.Contains(args.Item.Text)) 
        { 
            args.Item.Hidden = true; 
        } 
 
    } 
} 
 
For your reference, we have prepared a sample based on this, please refer below link. 
 
 
Please let us know, if you need any further assistance. 
 
Regards, 
Mohan Kumar R 



MC Mike Chafin October 7, 2020 07:39 PM UTC

That works, but comes with a heavy price as every menuitem (branch) has to be tested every time the menu is accessed.  

As my scenario is about the role a user has upon login you only want to set the menu hidden status once. Using the Create event allows you to accomplish that but the access to the menu tree is awkward.  

It would be much easier to just have a method, as I suggested, to work with the hidden property directly,. Please consider it in a future update. Another consideration is to remove the depreciated status of HideItem / ShowItem - I don't use deprecated methods in production code.

Again, what I'm using to accomplish my goal -

        var index = Menu.GetItemIndex(new MenuItem { Text = "Aggregates" });
        MainMenuItems[index[0]].Items[index[1]].Hidden = true;

Thanks for your time

Mike




MK Mohan Kumar Ramasamy Syncfusion Team November 2, 2020 03:13 PM UTC

Hi Mike, 
 
We have checked your requirement and we couldn’t able to remove deprecated from these methods. Since hiding and showing using methods is not a proper approach in Blazor. So, you can use hidden property of menu directive to hide the menu items. We have prepared the sample using hidden property by checking the logged in users. 
 
Please find the below code snippet. 
 
@using Syncfusion.Blazor.Navigations 
@using Syncfusion.Blazor.Buttons 
 
@{ 
 
    if (LoginType == "User") 
    { 
        isHidden = true; 
    } 
    else 
    { 
        isHidden = false; 
    } 
} 
<h4> Login Type : @LoginType</h4> 
<br /> 
<SfRadioButton Label="User" Name="logintype" Value="User" @bind-Checked="LoginType"></SfRadioButton> 
<SfRadioButton Label="Admin" Name="logintype" Value="Admin" @bind-Checked="LoginType"></SfRadioButton> 
<br /> 
<br /> 
<SfMenu TValue="MenuItem"> 
    <MenuItems> 
        <MenuItem Text="File" IconCss="em-icons e-file"> 
            <MenuItems> 
                <MenuItem Text="Open" IconCss="em-icons e-open"></MenuItem> 
                <MenuItem Text="Save" IconCss="em-icons e-save" Disabled="false"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Exit"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
 
        <MenuItem Text="Edit" IconCss="em-icons e-edit" Hidden="@isHidden"> 
            <MenuItems> 
                <MenuItem Text="Cut" IconCss="em-icons e-cut"></MenuItem> 
                <MenuItem Text="Copy" IconCss="em-icons e-copy"></MenuItem> 
                <MenuItem Text="Paste"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
 
        <MenuItem Text="View"> 
            <MenuItems> 
                <MenuItem Text="Toolbars"> 
                    <MenuItems> 
                        <MenuItem Text="Menu Bar"></MenuItem> 
                        <MenuItem Text="Bookmarks Toolbar"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Zoom"> 
                    <MenuItems> 
                        <MenuItem Text="Zoom In"></MenuItem> 
                        <MenuItem Text="Zoom Out"></MenuItem> 
                        <MenuItem Text="Reset"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Full Screen"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Tools"> 
            <MenuItems> 
                <MenuItem Text="Spelling & Grammar"></MenuItem> 
                <MenuItem Text="Customize" Hidden="@isHidden"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Options"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Help"></MenuItem> 
    </MenuItems> 
</SfMenu> 
 
@code { 
    private bool isHidden = false; 
    private string LoginType = "User"; 
} 
 
<style> 
    .e-radio-wrapper.e-wrapper { 
        margin-right: 15px; 
    } 
</style> 
 
Here, the Menu Items ‘Edit’ and Tools -> Customize is only visible to administrators. We have prepared the sample for the same and attached for your reference. Could you please check and get back to us if you have any other concerns? 
 
 
Regards, 
Mohan kumar R 
 



MC Mike Chafin November 3, 2020 10:24 AM UTC

The sample is over-simplistic.

 I have  10 different roles that a user may belong to and simply having a var for hidden and setting it does not work.  I need to allow the admin to have some leeway in a given role to see or not see a menu item.

This statement, "Since hiding and showing using methods is not a proper approach in Blazor."  Why?   I can't believe that the only acceptable approach is using binding to control the hidden property. It is a property and should be accessible through its getter/setter. Removing those methods, without an alternative, just makes it more difficult.

I use the following extension method, which uses a method from the SfMenu class.

        public static void SetMenuHidden(this SfMenu menu, string id, bool value)
        {
            var menuIds = id.Split(',');
            foreach (var idMenu in menuIds)
            {
                var menuIndex = menu.GetItemIndex(new MenuItem { Id = idMenu.Trim() }, true);
                if (menuIndex.Count > 0)
                {
                    
                    MenuItem lastMenu = null;
                    foreach (var index in menuIndex)
                    {
                        lastMenu = menu.Items[index];
                    }
                    lastMenu.Hidden = value;
                }
            }

        }

Mike



MK Mohan Kumar Ramasamy Syncfusion Team November 4, 2020 05:44 AM UTC

Hi Mike, 
 
We have checked your reported requirement. We have prepared the sample using hidden property by checking the logged in 10 different users. Please refer below code snippets. 
 
@using Syncfusion.Blazor.Navigations 
@using Syncfusion.Blazor.Buttons 
 
<h4> Login Type : @LoginType</h4> 
<br /> 
<SfRadioButton Label="User" Name="logintype" Value="User" @bind-Checked="LoginType"></SfRadioButton> 
<SfRadioButton Label="User1" Name="logintype" Value="User1" @bind-Checked="LoginType"></SfRadioButton> 
<SfRadioButton Label="Admin" Name="logintype" Value="Admin" @bind-Checked="LoginType"></SfRadioButton> 
<SfRadioButton Label="Tester" Name="logintype" Value="Tester" @bind-Checked="LoginType"></SfRadioButton> 
 
<br /> 
<br /> 
<SfMenu TValue="MenuItem"> 
    <MenuItems> 
        <MenuItem Text="File"> 
            <MenuItems> 
                <MenuItem Text="Open"></MenuItem> 
                <MenuItem Text="Save" Disabled="false"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Exit"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
 
        <MenuItem Text="Edit" Hidden="@checkHiddenItem("Edit")"> 
            <MenuItems> 
                <MenuItem Text="Cut"></MenuItem> 
                <MenuItem Text="Copy"></MenuItem> 
                <MenuItem Text="Paste"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="View"> 
            <MenuItems> 
                <MenuItem Text="Toolbars"> 
                    <MenuItems> 
                        <MenuItem Text="Menu Bar" Hidden="@checkHiddenItem("Menu Bar")"></MenuItem> 
                        <MenuItem Text="Bookmarks Toolbar" Hidden="@checkHiddenItem("Bookmarks Toolbar")"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Zoom"> 
                    <MenuItems> 
                        <MenuItem Text="Zoom In"></MenuItem> 
                        <MenuItem Text="Zoom Out"></MenuItem> 
                        <MenuItem Text="Reset"  Hidden="@checkHiddenItem("Reset")"></MenuItem> 
                    </MenuItems> 
                </MenuItem> 
                <MenuItem Text="Full Screen"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Tools"> 
            <MenuItems> 
                <MenuItem Text="Spelling & Grammar"></MenuItem> 
                <MenuItem Text="Customize" Hidden="@checkHiddenItem("Customize")"></MenuItem> 
                <MenuItem Separator="true"></MenuItem> 
                <MenuItem Text="Options" Hidden="@checkHiddenItem("Options")"></MenuItem> 
            </MenuItems> 
        </MenuItem> 
        <MenuItem Text="Help"></MenuItem> 
    </MenuItems> 
</SfMenu> 
 
@code { 
    private string LoginType = "User"; 
    private string[] userControl = new string[] { "Edit", "Customize", "Reset" }; 
    private string[] testerControl = new string[] { "Edit", "Bookmarks Toolbar" }; 
    private string[] user1Control = new string[] { "Options" }; 
    public bool checkHiddenItem(string itemText) 
    { 
        bool isHidden = false; 
        switch (LoginType) 
        { 
            case "User": 
                if (userControl.Contains(itemText)) 
                    isHidden = true; 
                else 
                    isHidden = false; 
                break; 
            case "User1": 
                if (user1Control.Contains(itemText)) 
                    isHidden = true; 
                else 
                    isHidden = false; 
                break; 
            case "Admin": 
                isHidden = false; 
                break; 
            case "Tester": 
                if (testerControl.Contains(itemText)) 
                    isHidden = true; 
                else 
                    isHidden = false; 
                break; 
 
        } 
        return isHidden; 
    } 
    } 
 
<style> 
    .e-radio-wrapper.e-wrapper { 
        margin-right: 15px; 
    } 
</style> 
 
Here, the all Menu Items is only visible to administrators. We have hidden menu items for below role.  
 
User –  Edit’ , Tools -> Customize and View -> Zoom -> Reset 
 
User1 –  Tools -> Options 
 
Tester - ‘Edit’ and  View -> Toolbars -> Bookmarks Toolbar  
 
We have prepared the sample for the same and attached for your reference. Could you please check and get back to us if you have any other concerns?  
  
  
Regards,  
Mohan kumar R  



MC Mike Chafin November 4, 2020 08:55 PM UTC

I appreciate the code but it contains .net framework 5 RC so was not executable in my environment. I don't install beta / RC codes.

I don't mean to be troublesome but the code as written is very expensive. What I mean by that is the method checkHiddenItem("text")  has to be called for every menu tree access that contains the call.  This should be something that is set once upon user login and the only way I know to achieve it is to set the menu once after authorization is complete and the user's roles are known.

Here's a short video of what happens with this code. I've added a console write at the start of the method displaying what is being tested.


As you can see, the provided code is executed by just mousing over the menu!

I'll still need my extension method to provide the functionality I need without the expense of calling unnecessary code each time a menu is accessed.

Mike




Attachment: 20201104_93943_f875efe3.rar


MK Mohan Kumar Ramasamy Syncfusion Team November 9, 2020 04:02 AM UTC

Hi Mike, 
 
We have checked your reported requirement. We have prepared the sample for dynamic update the datasource defends on 10 different users. Please refer below code snippets.  
 
@using Syncfusion.Blazor.Navigations 
@using Syncfusion.Blazor.Buttons 
 
<h4> Login Type : @LoginType</h4> 
 
<SfButton Content="User" @onclick="@(()=>Clicked("User"))"></SfButton> 
 
<SfButton Content="User1" @onclick="@(()=>Clicked("User1"))"></SfButton> 
 
<br /> 
<br /> 
<SfMenu TValue="MenuItem" Items="menuItems"> 
     
</SfMenu> 
 
@code { 
 
    private void Clicked(string name) 
    { 
        LoginType = name; 
        setMenu(name); 
    } 
    private string LoginType = "User"; 
 
    private List<MenuItem> menuItems; 
    protected override Task OnInitializedAsync() 
    { 
        setMenu(LoginType); 
        return base.OnInitializedAsync(); 
    } 
 
    private void setMenu(string loginType) 
    { 
        menuItems = new List<MenuItem>(); 
        if (loginType == "User") 
        { 
            menuItems.Add(new MenuItem { Text = "File", Items = new List<MenuItem> { new MenuItem { Text = "Open" }, new MenuItem { Text = "Save" }, new MenuItem { Separator = true }, new MenuItem { Text = "Exit" } } }); 
            menuItems.Add(new MenuItem { Text = "Edit", Items = new List<MenuItem> { new MenuItem { Text = "Copy" }, new MenuItem { Text = "Paste" } } }); 
        } 
        else if (loginType == "User1") 
        { 
            menuItems.Add(new MenuItem { Text = "File", Items = new List<MenuItem> { new MenuItem { Text = "Open" }, new MenuItem { Separator = true }, new MenuItem { Text = "Exit" } } }); 
            menuItems.Add(new MenuItem { Text = "Edit", Items = new List<MenuItem> { new MenuItem { Text = "Cut" }, new MenuItem { Text = "Copy" }, new MenuItem { Text = "Paste" } } }); 
        } 
    } 
} 
 
We have prepared the sample for the same and attached for your reference. Could you please check and get back to us if you have any other concerns?   
   
   
Regards,   
Mohan kumar R   



MC Mike Chafin November 9, 2020 12:09 PM UTC

Thanks for the support and I really do appreciate the code samples. There is no need to reply to this response

I do continue to wish that Syncfustion would consider reimplementation of the ShowItems and HideItems or a single method to work with the hidden attribute of the SfMenu control. Exposing the hidden attribute on the control should not have caused the depreciation of the ShowItems / HideItems. There are other Syncfustion controls that have methods that allow control through methods.

The following are code fragments of how I've implemented the menu system starting with the MainLayout.razor

@inherits MainLayoutBase
@using Syncfusion.Blazor.Navigations
<title>Churchfactotum CMS</title>

<div id="wrapper">
    <div class="col-lg-12 col-sm-12 col-md-12">
        <div id="header" class="header-section dock-menu sticky-top" >
            <ul class="header-list">
                <!--<li id="hamburger" class="icon-menu icon list" @onclick="@Toggle"></li>-->
                @*<input type="text" placeholder="Search..." class="search-icon list">*@
                <li class="left-header list">
                    <SfMenu @ref=@MainMenu TValue="MenuItem" ID="leftmenu" Items=@MainMenuItems Orientation="Syncfusion.Blazor.Navigations.Orientation.Horizontal">
                        <MenuEvents TValue="MenuItem" Created=@MainCreated />
                    </SfMenu>
                </li>
                <li class="right-header list"
                        <SfMenu @ref=@AccountMenu TValue="MenuItem" ID="rightmenu"  Items=@MenuItems Orientation="Syncfusion.Blazor.Navigations.Orientation.Horizontal">
                            <MenuEvents TValue="MenuItem" Created=@Created OnOpen=@OnOpen ItemSelected=@ItemSelected />
                        </SfMenu>
                    </li>
            </ul>
        </div>
        <div class="content">
            @Body
        </div>
    </div>
</div>

The base implementation of MainCreated using the extension method SetMenuHidden

        protected async Task MainCreated()
        {
            if (Identity.HasRole("OfficeStaff")
            {
               MainMenu.SetMenuHidden("members, groups, interactions, reports, giving",true);
            }
        }

The extension method SetMenuHidden -- used to replace the depreciated Sfmenu.ShowItems / HideItems 

        public static void SetMenuHidden(this SfMenu<MenuItem> menu, string id, bool value)
        {
            var menuIds = id.Split(',');
            foreach (var idMenu in menuIds)
            {
                var menuIndex = menu.GetItemIndex(new MenuItem { Id = idMenu.Trim() }, true);
                if (menuIndex.Count > 0)
                {
                    
                    MenuItem lastMenu = null;
                    foreach (var index in menuIndex)
                    {
                        lastMenu = menu.Items[index];
                    }
                    lastMenu.Hidden = value;
                }
            }

        }

The menu tree

        public List<MenuItem> MainMenuItems = new List<MenuItem>
        {
                new MenuItem {Id="brand", Text= "Churchfactotum CMS", Url = "/"},
                new MenuItem {Id="members", Text = "Members", IconCss = "icon-user icon", Items = new List<MenuItem>
                {
                    new MenuItem{Id="members-list", Text = "List", Url = "/Individuals" },
                    new MenuItem{Id="members-add", Text = "Add", Url = "/TabEdit"}
                }},
                new MenuItem {Id="groups", Text = "Groups", IconCss = "icon-bell-alt icon", Items = new List<MenuItem>
                {
                    new MenuItem{Id="groups-list", Text = "List", Url = "/Groups" },
                    new MenuItem{Id="groups-aggregates", Text = "Aggregates", Url = "/Aggregates" },
                    new MenuItem{Id="groups-mass-contact", Text = "Mass Contact", Url = "/Contact" },
                    new MenuItem{Id="groups-addendance", Text = "Attendance", Url = "/Attendance" }
                }},
                new MenuItem {Id="interactions", Text = "Interactions", IconCss = "icon-tag icon", Items = new List<MenuItem>
                {
                    new MenuItem{Id="interactions-my-outstanding", Text = "My Outstanding", Url = "/MyOutstanding/" + 14743},
                    new MenuItem{Id="interactions-outstanding", Text = "Outstanding", Url = "/Outstanding" },
                    new MenuItem{Id="interactions-by-status", Text = "By Status", Url = "/ByStatus" },
                    new MenuItem{Id="interactions-archived", Text= "Archived", Url = "/Archive" },
                    new MenuItem{Id="caregiving", Text = "Caregiving", Items = new List<MenuItem>
                    {
                        new MenuItem{Id="interactions-caregivers", Text = "Care Givers", Url = "/Caregiver" },
                        new MenuItem{Id="carerecipients", Text = "Care Recipients", Url = "/CareRecipients" },
                        new MenuItem{Id="interactions-carefacility", Text = "Care Facility", Url = "/CareFacility" }
                    }}
                }},
                new MenuItem {Id="reports", Text = "Reports", IconCss = "icon-bookmark icon", Items = new List<MenuItem>
                {
                    new MenuItem{Id="reports-overview", Text = "Overview", Url = "/overview" },
                    new MenuItem{Id="reports-attendance", Text = "Attendance", Url = "/attendance"},
                    new MenuItem{Id="reports-members", Text = "Members", Url = "/member-report" }
                }},
                new MenuItem {Id="giving", Text = "Giving", IconCss = "icon-picture icon", Items = new List<MenuItem>
                {
                    new MenuItem{Id="giving-my-giving", Text = "My Giving", Url = "/mygiving" },
                    new MenuItem{Id="giving-sevice", Text = "My Service", Url = "/myservice" }
                }},
        };




Loader.
Up arrow icon