Home > Net >  Can I render another partial view in a partial view by specifying relative path with Razor Pages?
Can I render another partial view in a partial view by specifying relative path with Razor Pages?

Time:12-15

I have a Razor Pages project (no Controller) with structure like this:

enter image description here

From the main Index.cshtml, I would render a partial view for its content depend on the Theme name, for example:

@* Default will be replaced with theme name *@
<partial name="Themes\Default\HomeContent" />

In HomeContent.cshtml, I would like to render many other partial views within its folder. However this wouldn't work:

<p>Content</p>

<partial name="_DefaultThemePartial" />

The engine only searches these locations (correct according to the documentation):

InvalidOperationException: The partial view '_DefaultThemePartial' was not found. The following locations were searched:

/Pages/_DefaultThemePartial.cshtml

/Pages/Shared/_DefaultThemePartial.cshtml

/Views/Shared/_DefaultThemePartial.cshtml

I have also tried <partial name="./_DefaultThemePartial" /> or <partial name=".\_DefaultThemePartial" /> or try putting them in a subfolder named Shared (within the Default folder). None of them works, only the above 3 locations are searched.

Is there anyway to render those partials without specifying the full path?

CodePudding user response:

I posted a proposal here. In the meantime, I found out you can expand the discovery mechanism by using IViewLocationExpander but I barely found any useful documentation on it.

public class PartialViewLocationExpander : IViewLocationExpander
{
    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        if (!context.Values.TryGetValue("FromView", out var fromView))
        {
            return viewLocations;
        }

        var folder = Path.GetDirectoryName(fromView) ?? "/";
        var name = context.ViewName;
        if (!name.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase))
        {
            name  = ".cshtml";
        }

        var path = Path.Combine(folder, name)
            .Replace('\\', '/');

        return viewLocations.Concat(new[] { path });
    }

    public void PopulateValues(ViewLocationExpanderContext context)
    {
        var ctx = context.ActionContext as ViewContext;
        if (ctx == null) { return; }

        var path = ctx.ExecutingFilePath;
        if (!string.IsNullOrEmpty(path))
        {
            context.Values["FromView"] = path;
            context.Values["ViewName"] = context.ViewName;
        }
    }
}

// Register
services.Configure<RazorViewEngineOptions>(options =>
{
    options.ViewLocationExpanders.Add(new PartialViewLocationExpander());
});

CodePudding user response:

From a response from my GitHub issue, there's a simpler solution without any extra code by adding the .cshtml at the end:

[...] This is by design. Partial view lookup is done in two ways:

  • By name

  • By file path

You've done the lookup by name (without file extension), if you want to use a relative path, make sure you specify the file extension.

<partial name="_DefaultThemePartial.cshtml" /> 
  • Related