Previous version of Asp.Net MVC had the concept of local view helpers that could contain HTML too. You see an example of this here. But this feature is removed in Asp.Net Core, but we have a better tool to do it. There might be a lot of other replacement solution for using
@helper, but in this case I’m going to use view components. The scenario I’m going to show is when we have some kind of tree data structure and we need to create a UI for that tree. This problem arises because on top of having normal code to call recursively, we have HTML and other element on page too. So we need some kind of mechanism that support HTML too.
Basic Tree Structure
Let’s assume we have a category for our blog. Each category has many children and those children can have their own children. Something like the hierarchical categories of WordPress. Here’s how our entity looks like.
Structure of our code is very simple, we have the
ParentId field which determine if the category top level category or not. Each category can have zero or more children.
Recursive Query To Build Tree Structure
What we need next is a set of raw entities, I’m going to assume we have this entities and they come from database. More important thing here is building a tree data structure form those entities. I wrote a recursive function to do this.
First we pass all the
GetBlogCategoryTree method. This method then is going to iterate over those entities and retrieve their children if they have any. Not only that but also children of those children and so on. As for our recursive call, which is the GetBlogCategoryChildren method, our base case is when the passed in category doesn’t have any children. If it does have children we’re going to retrieve them and and pass them to same method for retrieve theirs. Finally we return the categories to the caller, and caller return the top level categories only by filtering those categories who’s
ParentId is null.
Much Easier Way To Build Tree Structure
Although the above method works for building a tree, most of the times it should be avoided. There much easier way to do it, with this method, we offload building the tree to entity framework.
That’s it, that’s all we have to do to load the category children for each category. Important point here is that for it to work, we should first load all the categories into memory. Second important thing is that the table should have a self-referencing relationship as shown below.
Recursive View Component
For this section I’m not going to describe how we create a view component since it’s irrelevant to current discussion. If you don’t know how to you can read about it here. Let’s see how our view for the view component looks like.
The HTML detail for this view is not as relevant because every case is different. What is pertinent to this article starts at line 16. On that line we invoke the view component that we’re in at the present time. Each time we pass the model from the previous time to our view component and set the
isFirstCall to false. You also see that we check to see if the current category has any children by checking for null. This is what’s cause the recursion to exit when there’s no more children to traverse. Another important thing is
isFirstCall in our anonymous type. I can’t explain what
isFirstCall is for before explaining how our class for the view component looks like, let’s see.
isFirstCall because we only need the category collection the first time around. After first time, the model for this method is supplied through previous calls in the HTML part of the view component. The rest is trivial, we create a new view model again and pass that to the view component again. Notice that in cshtml part of the view component the
isFirstCall is always false. The only place that it’s true is well, when we first call it, here’s how it’s look like.
Here I call the view component in my home page as part of other view components.
In this post, we saw how to create a recursive data structure from raw data. Then we moved on to use view component recursively to create a UI for a tree. Let me know if you had any question or comments.