App


Reuse Templates and Code Tutorials

Tutorial HomeReuse

Reuse Shared Razor Helpers (CreateInstance / GetCode)

There are multiple ways to create shared Razor helpers:

  • public functions returning html
    ⭐ this is recommended in v12 and above as it works in Dnn ✅ and Oqtane ✅
  • the @helper command
    👍 this is simpler, but only works in Dnn ✅ but not in Oqtane ⛔

Razor helpers are functions that put HTML into the page. You can create them using either
@functions { dynamic helpername() { ... } }
or
@helper helpername() { ... }

You can also share them by placing them into a separate file (just remember to mark them public). This lets you put a bunch of tiny helpers into a helpers-library and use them from elsewhere. To achieve this, we use CreateInstance to get an object with these methods, and then use it.

The samples can differ based on your Razor base class or if you're running an old version.
Selected: Typed (2sxc 16+) Switch to Dynamic (Razor14 or below)

Using Shared Helpers / Libraries

The example takes a cshtml file using CreateInstance(...)and uses it as a central helpers library. The helper PreviewWithLightbox will generate a thumbnail, include Fancybox3 (only the first time) and ensure you can click it for a full image.

⬇️ Result | Source ➡️


@inherits Custom.Hybrid.RazorTyped

@{
  // Use GetCode in RazorTyped and path which is either
  // - absolute to App root
  // - relative to current file
  var lightbox = GetCode("/shared/Fancybox.cs");
}
@* Url to app root folder is in App.Folder.Url *@
@lightbox.PreviewWithLightbox(App.Folder.Url + "/app-icon.png")
<br>
@lightbox.PreviewWithLightbox(App.Folder.Url + "/reuse/demo.png", 200, 100)

Source Code of Fancybox.cs

using System.Linq;
using ToSic.Razor.Blade;
using ToSic.Sxc.Data;
using ToSic.Sxc.Images;
public class Fancybox: Custom.Hybrid.CodeTyped
{
  // Create an image which opens a larger version in a lightbox
  public IHtmlTag PreviewWithLightbox(string url, int width = 100, int height = 100, string classes = "", string label = null, bool figure = true)
  {
    var imgResizeSettings = Kit.Image.Settings(width: width, height: height);
    var html = ForLightbox(
      Kit.Image.Img(url, settings: imgResizeSettings),
      url,
      // Kit.HtmlTags.Img().Src(Link.Image(url, width: width, height: height)),
      classes: classes, label: label
    );

    // If figure is true, wrap the link in a figure tag
    return figure ? Kit.HtmlTags.Figure(html) as IHtmlTag : html;
  }

  public IHtmlTag ForLightbox(IResponsiveImage imgOrPic, string url, string group = null, string classes = "", string label = null)
  {
    // Make sure the fancybox is added to the page (will only add once)
    Kit.Page.Activate("fancybox4"); 

    // Create the link tag with the image inside
    var linkTag = Kit.HtmlTags.A().Data("fancybox", group ?? "gallery").Href(url).Class(classes).Data("caption", label).Wrap(
      imgOrPic
    );

    return linkTag;
  }

  public IHtmlTag Gallery(ITypedItem item, string fieldName, string group = null) {
    if (item == null || !item.ContainsKey(fieldName)) return null;

    var folder = item.Folder(fieldName);
    if (!folder.Files.Any()) return null;

    var t = Kit.HtmlTags;
    var gallery = t.Div();

    var imgList = folder.Files
      .Select(f => ForLightbox(Kit.Image.Picture(f, width: 200), f.Url, group: group, label: f.Metadata.Title))
      .ToArray();
    gallery = gallery.Add(imgList);
    return gallery;
  }
}

When you need a function in many places, it's best to put it into an own cshtml file and access it using GetCode(...) or CreateInstance.

The example takes a cshtml file with a QrPath function returning the url to a qr-code. It then accesses it using GetCode(...).

⬇️ Result | Source ➡️

Hello from lib: Hello!
@inherits Custom.Hybrid.RazorTyped
@using ToSic.Razor.Blade;

@{
  // Use GetCode in RazorTyped
  var lib = GetCode("SharedFunctions.cs");
}
<div>Hello from lib: @lib.SayHello()</div>
<div>
  <img loading="lazy" src='@lib.QrPath("https://2sxc.org")' width="75px">
</div>

Source Code of SharedFunctions.cs

public class SharedFunctions: Custom.Hybrid.Code14 {

  public string SayHello() {
    return "Hello!";
  }

  public string QrPath(string link) {
        // path to qr-code generator
        var qrPath = "//api.qrserver.com/v1/create-qr-code/?color={foreground}&bgcolor={background}&qzone=0&margin=0&size={dim}x{dim}&ecc={ecc}&data={link}"
            .Replace("{foreground}", App.Settings.QrForegroundColor.Replace("#", ""))
            .Replace("{background}", App.Settings.QrBackgroundColor.Replace("#", ""))
            .Replace("{dim}", App.Settings.QrDimension.ToString())
            .Replace("{ecc}", App.Settings.QrEcc)
            .Replace("{link}", link)
            ;
        return qrPath;
    }

}

Reuse Code with .cs Files

Starting with 2sxc 10.01 CreateInstance() can also be used with .cs files, from both razor pages as well as WebApi controllers. This allows you to create shared code which can be used in Razor and WebApi controllers.

Note that in 2sxc 16.05 we recommend you use GetCode() instead.

The example takes a cs file with shared code, but gets the class Second.

⬇️ Result | Source ➡️

Hello from FunctionsBasic: Hello from the second class!
@inherits Custom.Hybrid.RazorTyped

@{
  var lib = GetCode("FunctionsBasic.cs", className: "Second");
}
<div>Hello from FunctionsBasic: @lib.SayHello()</div>

Source Code of FunctionsBasic.cs

// Important notes: 
// - This class should have the same name as the file it's in
public class FunctionsBasic {

  public string SayHello() {
    return "Hello from shared functions!";
  }
}

// Example for another class in the same file
public class Second {
  public string SayHello() {
    return "Hello from the second class!";
  }
}

Often you may need context - like the Dnn or App objects. We made this easy by defining a base class you can inherit from, called Custom.Hybrid.Code14 (previously ToSic.Sxc.Dnn.DynamicCode). If you use that as your base-class, all existing context is automatically attached, allowing you to access variables like App.

⬇️ Result | Source ➡️

@inherits Custom.Hybrid.RazorTyped

@{
  var powerLib = GetCode("FunctionsWithContext.cs");
}
<div>
  <img loading="lazy" src='@powerLib.QrPath("https://2sxc.org")' width="75px">
</div>

Source Code of FunctionsWithContext.cs

// Important notes: 
// - This class should have the same name as the file it's in
// - This inherits from Custom.Hybrid.Code14
//   which will automatically provide the common objects like App, Dnn etc.
//   from the current context to use in your code
public class FunctionsWithContext: Custom.Hybrid.Code14 {

  public string QrPath(string link) {
        // path to qr-code generator
        var qrPath = "//api.qrserver.com/v1/create-qr-code/?color={foreground}&bgcolor={background}&qzone=0&margin=0&size={dim}x{dim}&ecc={ecc}&data={link}"
            .Replace("{foreground}", App.Settings.QrForegroundColor.Replace("#", ""))
            .Replace("{background}", App.Settings.QrBackgroundColor.Replace("#", ""))
            .Replace("{dim}", App.Settings.QrDimension.ToString())
            .Replace("{ecc}", App.Settings.QrEcc)
            .Replace("{link}", link)
            ;
        return qrPath;
    }

}

The mechanism above also works in WebApi Controllers (if you have 2sxc 10.01+). Note that specifically in WebApi you can only use GetCode(...) for .cs files.

⬇️ Result | Source ➡️

Click to see the result of a WebApi call with the shared code:

@inherits Custom.Hybrid.RazorTyped
@using ToSic.Razor.Blade;

<p>
  Click to see the result of a WebApi call with the shared code:
  <button type="button" class="btn btn-primary" onclick="callApiWithSharedCode(this)">
    Call WebApi
  </button>
</p>

<script>
  function callApiWithSharedCode(context) {
    $2sxc(context).webApi.fetchJson('app/auto/reuse/api/sharedcode/hello')
      .then(function (results) {
        console.log(results);
        alert(results);
      });
    return false;
  }
</script>

Source Code of SharedCodeController.cs

// Add namespaces for security check in Oqtane & DNN despite differences in .net core/.net Framework
// If you only target one platform, you can remove the parts you don't need
#if NETCOREAPP
using Microsoft.AspNetCore.Authorization; // .net core [AllowAnonymous] & [Authorize]
using Microsoft.AspNetCore.Mvc;           // .net core [HttpGet] / [HttpPost] etc.
#else
using System.Web.Http;                    // .net 4.5 [AllowAnonymous] / [HttpGet]
using DotNetNuke.Web.Api;                 // [DnnModuleAuthorize] & [ValidateAntiForgeryToken]
#endif

[AllowAnonymous]                          // all commands can be accessed without a login
[ValidateAntiForgeryToken]                // protects API from users not on your site (CSRF protection)
public class SharedCodeController : Custom.Hybrid.Api14 // see https://r.2sxc.org/CustomWebApi
{
  [HttpGet]
  [AllowAnonymous]
  public string Hello()
  {
    var shared = CreateInstance("../FunctionsBasic.cs");
    return shared.SayHello();
  }
}

// The next line is for 2sxc-internal quality checks, you can ignore this
// 2sxclint:disable:no-dnn-namespaces - 2sxclint:disable:no-web-namespace

Source Code of FunctionsBasic.cs

// Important notes: 
// - This class should have the same name as the file it's in
public class FunctionsBasic {

  public string SayHello() {
    return "Hello from shared functions!";
  }
}

// Example for another class in the same file
public class Second {
  public string SayHello() {
    return "Hello from the second class!";
  }
}