2

The HTML menu rendered from my helper function is

  • Category 1
    • Category 2
  • Category 2
  • Category 3
  • Category 4

I have set Category 2 as a child category of Category 1.

Unfortunately, the current HTML helper displays again Category 2 as a parent afterwards.

Id  ParentCategoryId Description
1   NULL             Category 1
2   1                Category 2 
3   NULL             Category 3 
4   NULL             Category 4 
5   NULL             Category 5

How should I modify my helper function?

@{ Func<dynamic, IHtmlContent> ShowMenu(List<Category> cats) =>
    @<ul>
        @foreach (var cat in cats)
        {
            <li>
                @cat.Description
                @if (cat.Childs != null && cat.Childs.Any())
                {
                    @ShowMenu(cat.Childs)(null)
                }
            </li>
        }
    </ul>; 
}

The category model used is as

public class Category
{
    public int Id { get; set; }

    public int? ParentCategoryId { get; set; }
    public Category ParentCategory { get; set; }

    [ForeignKey("ParentCategoryId")]
    public List<Category> Childs { get; set; }

    public string Description { get; set; }
}

The html menu is displayed in razor via

@ShowMenu(Model.Categories)(null)

where

Model.Categories = context.Categories.ToList()

Update: Thanks to the helpfull answers given, it seems i should also pass the parent category as a parameter.

@{ Func<dynamic, IHtmlContent> ShowMenu(List<Category> cats, Category parent) =>
@<ul>
    @foreach (var cat in cats)
    {
        <li>
            @cat.Description
            @if (cat.Childs != null && cat.Childs.Any())
            {
                @ShowMenu(cat.Childs, cat)(null)
            }
        </li>
    }
</ul>; }
3
  • I think the problem can't be cured in your helper function. I suppose the problem is in the first-level call. Please show that code, too Commented Jan 24, 2021 at 9:36
  • @Turo Thank you, just updated with required info Commented Jan 24, 2021 at 9:41
  • You need to pass a list of categories with ParentCategoryId == null to your ShowMenu.And the categories need to contain all the childs and childs's childs. Commented Jan 25, 2021 at 8:03

2 Answers 2

1

cats holds all your categories, apparently. Note that your function will show all the elements of your list, so, if your outer list contains all elements, even the inner elements, then the issue you experience happens. Now, a simple solution is to change your function, so that it receives a Category parent parameter as well and you pass a null at the outer call and cat at the recursive call. Now, wrap an if around your <li></li> node, to check whether ParentCategoryId matches the parent param's value we have just discussed. If that condition evaluates to true, then the li will be displayed. If not, then it will not be displayed.

EDIT

I have wrapped the if I suggested around the logic. Since I am unfamiliar with this syntax, it is possible that the code below is not the way this should be written, but I'm convinced that the idea behind this code is the one that needs to be applied.

@{ Func<dynamic, IHtmlContent> ShowMenu(List<Category> cats, Category parent) =>
@<ul>
    @foreach (var cat in cats)
    {
        @if (((parent == null) && (cat.ParentCategoryId == null)) || (parent?.Id == cat.ParentCategoryId)))
        {
            <li>
                @cat.Description
                @if (cat.Childs != null && cat.Childs.Any())
                {
                    @ShowMenu(cat.Childs, cat)(null)
                }
            </li>
        }
    }
</ul>; }
Sign up to request clarification or add additional context in comments.

6 Comments

I think i just had an StackOverflow exception. Still trying on how to implement it
@IAmNotARobot you might want to edit your question with your current code and data.
Thank you for your efforts, could you please check the update? I am really confused with the if statement check you mention
@IAmNotARobot I have looked into your question and you have implemented half of the idea I have presented. I have edited my answer with the way I think the code should look alike, but since I am unfamiliar with this syntax, it's possible that the code above would not be syntactically correct. However, I am fairly sure that the idea should be the one that you need to apply.
Works like a charm! If you mind replace with this one (contains null checking also) @if ((parent == null && cat.ParentCategoryId == null) || parent?.Id == cat.ParentCategoryId)
|
1

Only pass the roots categories like this:

 Model.Categories = context.Categories.Where(c => c.ParentCategoryId == null).ToList()

6 Comments

Thank you but this will not work if Category 3 is a child of Category 2 also
couldn't have done it this nice being a java guy :-)
I didn't tried but I think it would work, have you tried ?
@IAmNotARobot don't think so, give it a try
By doing so, only Categories 1,3,4 and 5 are rendered
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.