Skip to content

Implementation of a "Parent-Child"-Controller system where child-controllers are hidden

Notifications You must be signed in to change notification settings

st3v3y/Parent-Child-Controller-Routing

Repository files navigation

Parent-Child-Controller-Routing

##What is it for? It is an example implementation of a "Parent-Child"-Controller system. Imagine we have a single controller ("HomeController") which is called on every page request. But we also want to work with other controllers as parts of the page and use them to display forms for example ("HiddenFormController" and "AnotherHiddenFormController").

If we would only want to display a controller action result, it would be easy to accomplish by using @Html.Action("Index", "HiddenForm"). But we want to use a form and display action result as child part of the "Home"-Controller again. And that it is actually a bit more tricky.

This is mainly what this implementation solves.

The example code

The example constists of two parts. The first part demonstrates "Form Handling". The second one demonstrates the "Child-Routing".

Form handling

The first part is shown on the first page which is split in two sections:

Standard controller

On top we have a default plain form without any changes.

standard controller

If push the button we end up on a new blank page without the nice design and all "Parent"-Controllers which we loaded on the previous page.

standard controller result

Child controller

Instead, the bottom part demonstrates the functionality of this project.

hidden child controller

Once we pushed a button we'll see the following result, staying on the same page:

hidden child_controller result

Child-Routing

What does "child"-routing mean?

It means that you can seperate your "Parent"/"Home"-Controller by several "Child"-Controllers and you still have the opportunity to give them states by using special url-routes.

Simple example: Your "Home"-Controller has a "Child"-Controller which lists many blog articles and you need a paging-mechanism. You don't want to use the static MVC routing to specify a new route for the page number and you don't want to use bad looking query parameters which you have to extract from the url. So, you can just specify a "Child"-Route and catch automatically the correct page number from a clean URL like "http://mywebsite.com/blog/list/3"

How it works

It works! Really! That's it! ;-)

No, it is complicated to explain in a view sentences because many things correlate to each other. For a detailed answer you have to have a look into the code. For a more or less simple answer just keep on reading...

All starts with an URL like the one above (http://mywebsite.com/blog/list/3). The /blog/-Part stands for the current page. It means, we are actually still on our "Home"-Controller but we are displaying a template specially defined for the "Blog"-Page. It is also possible to define Sub-Pages like "http://mywebsite.com/blog/latest". Until this point we just call a defined template which renders some "Child"-Controllers.

The next part of the URL is a bit more tricky: When you render a "Child"-Action you can specify an identifier. This identifier is needed to tell the route that the following parameter(s) is/are targeting this action. By specifying a "Child-Route" (by simply putting an attribute) like [ChildRoute("{year}/{pageNumber}")] you can call an URL like "http://mywebsite.com/blog/list/2017/2" which means that all "Child"-Controller-Actions rendered with the identifier "list" will be called with parameters year = "2017" and pageNumber = 2. The complete action signature could look like this:

[ChildRoute("{year}/{pageNumber}")]
public ActionResult BlogEntryList(int? year, int pageNumber = 1)
{
...
}

The order of multiple "Child-Routes" does not matter. An URL like "http://mywebsite.com/blog/identifier1/param1/identifier2/param1/param2" would exactly work the same like
"http://mywebsite.com/blog/identifier2/param1/param2/identifier1/param1"

Child-Routing: Defaults

Given you have an URL like "http://mywebsite.com/blog/list/2017/2" the "year"-parameter acts like a filter. You're just displaying blog entries of 2017. In some cases it would be more convenient to use an "default" instead (which is not "-1" or "0" because it would't look very professional). So you wish to have a default like "all" with an URL like: "http://mywebsite.com/blog/list/all/2".

Important marginal note:

Currently it is not possible to just leave this parameter simply away because it would break the validation whether the URL is valid!

Then this is your action would look like:

[ChildRoute("{year}/{pageNumber}")]
[ChildRouteDefault("year", "all")] //Add this line
public ActionResult BlogEntryList(int? year, int pageNumber = 1)
{
...
}

Child-Routing: Constraints

Very annoying: Your colleague played around a little bit and found out he can also call your page with "http://mywebsite.com/blog/list/1955/2" and it displays the same page but with an empty list. He put this link on his website and laughs about you because google has already indexed this URL from his website and it is ranked in the first place. Damn! (Not really realistic scenario but actually what could happen) So, it's too late to remove the Google index immediately. But next time you can prevent the mistake by simply defining a constraint:

[ChildRoute("{year}/{pageNumber}")]
[ChildRouteDefault("year", "all")]
[ChildRouteBlogEntryYearConstraint("year")] //Add this line
public ActionResult BlogEntryList(int? year, int pageNumber = 1)
{
...
}
public class ChildRouteBlogEntryYearConstraintAttribute : ChildRouteConstraintAttribute
{
    public ChildRouteBlogEntryYearConstraintAttribute(string routeParamName) 
        : base(routeParamName)
    {
    }

    public override bool IsValidRouteValue(object routeValue)
    {
        var value = routeValue as int?;
        if (value == null)
        {
            return false;
        }
        return new BlogEntryRepository().GetAllBlogEntries().Any(x => x.Date.Year == value.Value);
    }
}

About

Implementation of a "Parent-Child"-Controller system where child-controllers are hidden

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published