9

Say I have a Feature layer module in my Helix compliant solution, e.g. the LinkMenu component in Sitecore.Feature.Navigation module from Habitat.

Assuming I am happy with the way this works from a functional perspective for all of the sites in my solution, but Site A requires different html markup from Site B.

What is the best way to override the cshtml rendering for each site? Everything else, Controller, Model templates etc would all work ok without modifying anything.

9

You can make use of MVC Areas (support for which was introduced in Sitecore 8.1).

There are a number of strategies OOTB for resolving the area of a site, such as resolving by rendering parameters or layout definition. It's also possible to add your own strategy, we set your Area definition per site on the <site> node in using a similar processor as specified in this article by Kevin Brechbühl. Use whatever strategy works for you best.

It's then possible to set the Area in the config of your project, and essentially create overrides of the views per project, you just need to ensure that Views are placed in the Areas folder and the follows the same structure as your Feature:

Site-Project - Areas - [Site-Area-Name] - Views - FeatureName - view.cshtml

Your project will require a dependency on the Feature (due to the model declaration and usage in your View) but this is a perfectly valid dependency within the principles.

4

One way you could solve this is by using a global action filter to decide which view to load based on the current request. Then you don't have to check it in each controller.

Haven't tested this, but something like this:

public sealed class RequestBasedViewAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        base.OnResultExecuting(filterContext);

        // Get current view
        string viewName = (filterContext.Result as ViewResult)?.ViewName;

        // Alter viewName based on current request
        viewName = AlterViewNameBasedOnRequest(viewName);

        // Check if the view exists
        if (ViewExists(viewName))
        {
            // Update the context
            filterContext.Result = new ViewResult
            {
                ViewName = viewName
            };
        }
    }
}

Don't forget to register this as a global filter:

GlobalFilters.Filters.Add(new RequestBasedViewAttribute());
  • This is a great solution! +1 – Pete Navarra Jan 18 '17 at 16:51
1

I only have some cursory experience looking over Habitat, but my thought would be to have two CSHTML files in your module, one for site A and one for site B. These should be able to be wired up to the common controller/model since you said that'd work in both places. Then make a rendering for site A and site B and use as appropriate.

  • This approach violates one of the Helix dependency principles by adding a dependency to the module on both of the sites which would be in the project layer. – Matthew Dresser Jan 18 '17 at 16:56
  • 3
    @MatthewDresser I don't agree. This provides two simple solutions to one business problem without overcomplicating things. The renderings belongs to one business domain and should therefore remain in a single module - there is not necessarily a dependency from rendering 2 to site B - just as there would be no dependency from rendering 1 to Site A. Keep in mind: One entire Helix solution cover requirements of a business - even if the business is split into multiple entities. E.g. The Navigation Feature would still be covering navigation even if it cover two business entities. – Eldblom Jan 19 '17 at 10:22
-3

We recently started developing same scenario in Helix-

We already developed Site A with Helix and now we got requirement to create another site with name "Site B" in same Sitecore instance.

So how we are implementing this - we are creating folder with site name under the feature folder for example in navigation we have created folder /views/Navigation/SiteB and all the view renderings we created or copied under /views/Navigation/SiteB location.

Now In controller side we created a extension method which is giving us current site. And based on current side we are calling the view in controller action method. I am sharing the code for breadcrumb how we have implemented.

public ActionResult Breadcrumb() 
        { 
            var items = this._navigationRepository.GetBreadcrumb();   

            if (site) 
            { 
                return this.View("SiteB/Breadcrumb", items); 
            } 
            else 
            { 
                return this.View("Breadcrumb", items); 
            } 
        }

For "SiteA" code will work as it was working and for SiteB we did this small change wherever it's needed.

So above example is only for if we have controller renderings and if we have only view renderings then we are simply creating view rendering under the site specific folder and there is no need to worry about the code.

As per this approach we don't need to make much changes in code side.

I found only this way to handle multisite with multiple nodes in Helix.

  • 1
    My first impression about this approach, is that it immediately adds a dependency to the Navigation module on both Site A and Site B which violates the direction of dependencies. – Matthew Dresser Jan 18 '17 at 16:37
  • This would absolutely violate the dependency principles - as the feature module directly references site B - and not defines the business objective of the alternate rendering. – Eldblom Jan 19 '17 at 10:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.