App


Quick Reference / Cheat-Sheet

Tutorial HomeQuick-Ref
#1 Quick Reference for all APIs

Quick Reference for all APIs

This is a quick CheatSheet to provide you with a reference to most APIs you'll use.

This uses a base class of @inherits Custom.Hybrid.RazorTyped. Most samples would not work on older Razor base classes such as @inherits Custom.Hybrid.Razor14 as that provides different objects and methods.

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)

Razor Basics @...

Common basics for working with Razor.

  1. Show firstName (string): Terry
  2. Show birthday (DateTime):
    4/28/1948 12:00:00 AM
  3. Show birthday (DateTime, formatted):
    1948-04-28
  4. Show age (int): 76
  5. Show decades (int): 7
  6. Show decadesFloat (float): 7.6
  7. Show decadesInt (int): 7
@inherits Custom.Hybrid.Razor14

@{
  // Create variables using var; compiler detects the type
  var firstName = "Terry";
  var birthday = new DateTime(1948, 04, 28);
  var age = DateTime.Now.Year - birthday.Year;
  var decades = age / 10;             // int division - decades will be int
  var decadesFloat = (float)age / 10; // float div - so it be float
  int decadesInt = (int)decadesFloat; // this drops the decimal places
}
<ol>
  <li>Show firstName (string): @firstName</li>
  <li>
    Show birthday (DateTime): <br>
    @birthday
  </li>
  <li>
    Show birthday (DateTime, formatted): <br>
    @birthday.ToString("yyyy-MM-dd")
  </li>
  <li>Show age (int): @age</li>
  <li>Show decades (int): @decades</li>
  <li>Show decadesFloat (float): @decadesFloat</li>
  <li>Show decadesInt (int): @decadesInt</li>
</ol>
  1. Basic set variable and show

    Create variables and show using @variableName

⬇️ Result | Source ➡️

Show old/not old based on Terry's age using @if and @else: this guy is old

Ternare show old/not old based on age ? ... : : this guy is old

@inherits Custom.Hybrid.Razor14

@{
  const int old = 60;
  const string isOld = "this guy is old";
  const string notOld = "this guy is not old";
  var age = 62;
}
<p>
  Show old/not old based on Terry's age using
  <code>@@if</code> and <code>@@else</code>:
  @if (age > old) {
    @isOld
  } else {
    @notOld
  }
</p>
<p>
  Ternare show old/not old based on age <code>? ... : </code>:
  @(age > old ? isOld : notOld)
</p>
  1. Razor Conditions such as if, if-else and ? :

    if/else and ternary operator (condition ? true : false)

⬇️ Result | Source ➡️

Loop through Pets using
@foreach (var thing in list)

  • dog
  • cat
  • mouse

Loop through Pets using
@for (counter; condition; increment)

  • dog - owned by Daniel
  • cat - owned by John
  • mouse - owned by Markus
@inherits Custom.Hybrid.Razor14

@{
  var pets = new string[] { "dog", "cat", "mouse"};
  var owners = new string[] { "Daniel", "John", "Markus"};
}
<h4>
  Loop through Pets using <br>
  <code>@@foreach (var thing in list)</code>
</h4>
<ul>
  @foreach (var pet in pets) {
    <li>@pet</li>
  }
</ul>
<h4>
  Loop through Pets using <br>
  <code>@@for (counter; condition; increment)</code>
</h4>
<ul>
  @for(var i = 0; i < pets.Length; i++) {
    <li>@pets[i] - owned by @owners[i]</li>
  }
</ul>
  1. Loops - for and foreach

    Loops using for() and foreach()

⬇️ Result | Source ➡️

Output values (HTML-source shown)

  • this is text, it doesn't have tags
  • this string <em>has</em> html <strong>tags</strong>

Output values (HTML is used; maybe security risk)

  • this is text, it doesn't have tags
  • this string has html tags
@inherits Custom.Hybrid.Razor14

@{
  var normalText = "this is text, it doesn't have tags";
  var htmlText = "this string <em>has</em> html <strong>tags</strong>";
}
<h4>Output values (HTML-source shown)</h4>
<ul>
  <li>@normalText</li>
  <li>@htmlText</li>
</ul>
<h4>Output values (HTML is used; maybe security risk)</h4>
<ul>
  <li>@Html.Raw(normalText)</li>
  <li>@Html.Raw(htmlText)</li>
</ul>
  1. Work with HTML Output

    Learn the difference of showing variables with @variable and @Html.Raw(variable), and re-use very simple snippets

⬇️ Result | Source ➡️

You can just use any Emoji in your source. Or you pick them with ternary operations.

  • This is just an emoji: 🚀
  • Pick emoji from true ⇒ ✔️
  • Pick emoji from true ⇒ ❌
@inherits Custom.Hybrid.Razor14

<p>
  You can just use any Emoji in your source.
  Or you pick them with ternary operations.
</p>
<ul>
  <li>This is just an emoji: 🚀</li>
  <li>Pick emoji from true ⇒ @(true ? "✔️" : "❌")</li>
  <li>Pick emoji from true ⇒ @(false ? "✔️" : "❌")</li>
</ul>
  1. Working with 😃 Emojis / Emoticons

    Show Emojis in your output or use them for showing true/false

A common task is to link to a specific page or to stay on the same page but with different URL parameters. 

⬇️ Result | Source ➡️

Normally you would use the following in an href attribute.

Current Page

  1. This page URL:
    https://oqt-apps-dev.2sxc.org/razortutorial12
  2. This page url params:
    id=46290&tut=quickref
  3. Link this page without params:
    https://oqt-apps-dev.2sxc.org/razortutorial12
  4. Link this page with same params:
    https://oqt-apps-dev.2sxc.org/razortutorial12?id=46290&tut=quickref
  5. Link this page with same params:
    https://oqt-apps-dev.2sxc.org/razortutorial12?id=46290&tut=quickref
  6. Link this page with more params:
    https://oqt-apps-dev.2sxc.org/razortutorial12?id=46290&tut=quickref&another&more=27&more=42
  7. Link this page remove params:
    https://oqt-apps-dev.2sxc.org/razortutorial12?id=46290&tut=quickref
  8. Link this page replace params:
    https://oqt-apps-dev.2sxc.org/razortutorial12?id=46290&tut=quickref&quick-ref-typed=test

Another Page

This only works if you don't have a page 12, otherwise you'll see an error in the url.

  1. Link to page 12 without params:
    https://oqt-apps-dev.2sxc.org/admin/profiles
  2. Link to page 12 with current params:
    https://oqt-apps-dev.2sxc.org/admin/profiles?id=46290&tut=quickref
@inherits Custom.Hybrid.RazorTyped

<p>
  Normally you would use the following in an <code>href</code> attribute.
</p>
<h4>Current Page</h4>
<ol>
  <li>This page URL: <br>
    <code>@MyPage.Url</code>
  </li>
  <li>This page url params: <br>
    <code>@MyPage.Parameters</code>
  </li>
  <li>Link this page without params: <br>
    <code>@Link.To(parameters: "")</code>
  </li>
  <li>Link this page with same params: <br>
    <code>@Link.To(parameters: MyPage.Parameters)</code>
  </li>
  <li>Link this page with same params: <br>
    <code>@Link.To(parameters: MyPage.Parameters)</code>
  </li>
  <li>Link this page with more params: <br>
    <code>@Link.To(parameters: MyPage.Parameters
      .Add("another")
      .Add("more", 27)
      .Add("more", 42)
    )</code>
  </li>
  <li>Link this page remove params: <br>
    <code>@Link.To(parameters: MyPage.Parameters
      .Remove("quick-ref-typed")
    )</code>
  </li>
  <li>Link this page replace params: <br>
    <code>@Link.To(parameters: MyPage.Parameters
      .Set("quick-ref-typed", "test")
    )</code>
  </li>
</ol>

<h4>Another Page</h4>
<p>
  This only works if you don't have a page 12, 
  otherwise you'll see an error in the url.
</p>
<ol>
  <li>Link to page 12 without params: <br>
    <code>@Link.To(pageId: 12)</code>
  </li>
  <li>Link to page 12 with current params: <br>
    <code>@Link.To(pageId: 12, parameters: MyPage.Parameters)</code>
  </li>
</ol>

Reuse Razor and CSharp Files

Don't-Repeat-Yourself (DRY) by reusing code in Razor and helper C# files.

⬇️ Result | Source ➡️

Reuse some functions inside this file

  • Boolmoji(true) ⇒ 👍🏽
  • Boolmoji(false) ⇒ 👎🏽
  • Random number function: 23
  • Random number function: 2
  • this is a bold item in a list using a Template Delegate
  • another bold item in a list using a Template Delegate
@inherits Custom.Hybrid.Razor14

@functions {
  string Boolmoji(bool value) { return value ? "👍🏽" : "👎🏽"; }
  
  // Variable keeping the random number generator
  private Random generator = new Random(DateTime.Now.Second);

  // Get random number between 0 and 100
  public int Random100() {
    return generator.Next(0, 100);
  }
}
@{
  // Simple Hybrid (Dnn/Oqtane) Template Delegate
  Func<string, dynamic> BoldLi = @<li>
    <strong>
      @Html.Raw(item)
    </strong>
  </li>;
}
<h4>Reuse some functions inside this file</h4>
<ul>
  <li>Boolmoji(true) ⇒ @Boolmoji(true)</li>
  <li>Boolmoji(false) ⇒ @Boolmoji(false)</li>
  <li>Random number function: @Random100()</li>
  <li>Random number function: @Random100()</li>
  @BoldLi("this is a bold item in a list using a Template Delegate")
  @BoldLi("another bold item in a list using a Template Delegate")
</ul>
  1. Code - Function and Similar Helpers

    Normal C# functions are the basic block for reusing code. 

  2. Reuse Shared Razor Helpers (CreateInstance / GetCode)

    Use a shared razor file to hold multiple functions / helpers, and call them one-by-one as needed. 

  3. Reuse Code and Razor Templates

    Explains the basics of re-using code across templates and code files.

⬇️ Result | Source ➡️

Call some external Razor files



@inherits Custom.Hybrid.RazorTyped

<h4>Call some external Razor files</h4>
<ol>
  <li>@Html.Partial("../../razor/partial/line.cshtml")</li>
  <li>@Html.Partial("../../razor/partial/line.cshtml")</li>
</ol>

Source Code of line.cshtml

@inherits Custom.Hybrid.Razor14
<hr style="height: 10px; border: 1; box-shadow: inset 0 9px 9px -3px rgba(11, 99, 184, 0.8); border-radius: 5px;">
  1. Reuse Shared Templates with Html.Partial and MyModel

    Reuse Razor files by calling them using @Html.Partial and passing parameters in DynamicModel (or  MyModel).

⬇️ Result | Source ➡️

Call some external Razor files

  1. Hello, this is the first line
  2. Second line!
  3. Third line!
@inherits Custom.Hybrid.RazorTyped


<h4>Call some external Razor files</h4>
<ol>
  <li>
    @Html.Partial("../../razor/partial/Box Typed.cshtml", new {
      Label = "Hello, this is the first line"
    })
  </li>
  <li>
    @Html.Partial("../../razor/partial/Box Typed.cshtml", new {
      Label = "Second line!",
      Color = "red"
    })
  </li>
  <li>
    @{
      var label = "Third line!"; // if variable name is the same...
      var color = "green";  // if variable name is the same...
    }
    @Html.Partial("../../razor/partial/Box Typed.cshtml", new { label, color })
  </li>
</ol>

Source Code of Box Typed.cshtml

@inherits Custom.Hybrid.RazorTyped
@{
  // pick up parameters which were passed in
  var label = MyModel.String("Label");
  var color = MyModel.String("Color", fallback: "black"); // default to "black" if not provided
}
<div style="border: solid 2px @color">
  @label
</div>
  1. Reuse Shared Templates with Html.Partial and MyModel

    Reuse Razor files by calling them using @Html.Partial and passing parameters in DynamicModel (or  MyModel).

Reuse Partial Razor and functions from Helper Code files.

⬇️ Result | Source ➡️

Call some Code in External CSharp Files

  1. Hello from shared lib: Hello!
  2. QR Code from shared lib
@inherits Custom.Hybrid.RazorTyped

@{
  // helper library to say hello & create QR codes
  var qrLib = GetCode("../../code/reuse/SharedFunctions.cs");
}
<h4>Call some Code in External CSharp Files</h4>
<ol>
  <li>Hello from shared lib: @qrLib.SayHello()</li>
  <li>
    QR Code from shared lib <br>
    <img loading="lazy" src='@qrLib.QrPath("https://2sxc.org")' width="75px">
  </li>
</ol>

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;
    }

}
  1. Reuse Shared Razor Helpers (CreateInstance / GetCode)

    Use a shared razor file to hold multiple functions / helpers, and call them one-by-one as needed. 

Razor doesn't know about types inside code files, so you need some tricks to work with objects which are passed around. 

⬇️ Result | Source ➡️

Working with anonymous objects

The only way to get values from an anonymous object created elsewhere is using AsTyped(... as object). Everything else will 🚫.

  1. Anon: { Name = John, Age = 30 }
  2. Anon Type: <>f__AnonymousType0`2[System.String,System.Int32]
  3. aTyped.Name ✅: John
  4. Anon.Name 🚫: ⚠️ Exception: 'object' does not contain a definition for 'Name'
  5. anonDyn.Name 🚫: ⚠️ Exception: 'object' does not contain a definition for 'Name'
  6. aTypedDyn.Name 🚫: ⚠️ Exception: 'ToSic.Sxc.Data.Internal.Typed.WrapObjectTyped' does not contain a definition for 'Get'

Working with ITyped objects

If the returned object is based on ITyped, things are a bit easier. But you must still ensure that Razor knows the type.

  1. Name ✅: John
  2. Name (string) ✅: John

Working with real objects

If the returned object is based on a class, things are a bit easier.

  1. Person: ReturnObjects+Person
  2. Person Type: ReturnObjects+Person
  3. Person.Name ✅: John
  4. pTyped.Get("Name") ✅: John
  5. pTyped.String("Name") ✅: John
  6. pTypedDyn.Get("Name") 🚫: ⚠️ Exception: 'ToSic.Sxc.Data.Internal.Typed.WrapObjectTyped' does not contain a definition for 'Get'

Working with dictionaries

If the returned object is based on a dictionary, things are a bit easier. This is because dynamic will be able to work with it, but it's not type safe.

  1. dicDyn: System.Collections.Generic.Dictionary`2[System.String,System.String]
  2. dicDyn Type: System.Collections.Generic.Dictionary`2[System.String,System.String]
  3. dicDyn Count ✅: 2
  4. dicDyn["Name"] ✅: John
  5. dic: System.Collections.Generic.Dictionary`2[System.String,System.String]
  6. dic Type: System.Collections.Generic.Dictionary`2[System.String,System.String]
  7. dic Count ✅: 2
  8. dic["Name"] ✅: John
@inherits Custom.Hybrid.RazorTyped


@{
  // helper library which will return object
  var lib = GetCode("../../reuse/ReturnObjects.cs");
}
<h4>Working with anonymous objects</h4>
<p>
  The only way to get values from an anonymous object 
  created elsewhere is using 
  <code>AsTyped(... as object)</code>.
  Everything else will 🚫.
</p>
@{
  // Various ways to (try) to get values from an anonymous object
  var anon = lib.GetAnonymous();
  var anonDyn = anon as dynamic;
  var aTypedDyn = AsTyped(anon);
  var aTyped = AsTyped(anon as object);
}
<ol>
  <li>Anon: @anon</li>
  <li>Anon Type: @anon.GetType()</li>
  <li>aTyped.Name ✅: @aTyped.Get("Name")</li>
  <li>Anon.Name 🚫: @TryGet(() => anon.Name)</li>
  <li>anonDyn.Name 🚫: @TryGet(() => anonDyn.Name)</li>
  <li>aTypedDyn.Name 🚫: @TryGet(() => aTypedDyn.Get("Name"))</li>
</ol>

<h4>Working with <code>ITyped</code> objects</h4>
<p>
  If the returned object is based on <code>ITyped</code>, things are a bit easier.
  But you must still ensure that Razor knows the type.
</p>
@{
  var typedDyn = lib.GetTyped();          // Razor sees this as dynamic
  var typed = AsTyped(typedDyn as object);// this is now typed
}
<ol>
  <li>Name ✅: @typed.Get("Name")</li>
  <li>Name (string) ✅: @typed.String("Name")</li>
</ol>

<h4>Working with real objects</h4>
<p>
  If the returned object is based on a class, things are a bit easier.
</p>
@{
  // Various ways to (try) to get values from real object
  // note that for Razor, the type is `dynamic`
  var person = lib.GetPerson();
  var pTypedDyn = AsTyped(person);
  var pTyped = AsTyped(person as object);
}
<ol>
  <li>Person: @person</li>
  <li>Person Type: @person.GetType()</li>
  <li>Person.Name ✅: @person.Name</li>
  <li>pTyped.Get("Name") ✅: @pTyped.Get("Name")</li>
  <li>pTyped.String("Name") ✅: @pTyped.String("Name")</li>
  <li>pTypedDyn.Get("Name") 🚫: @TryGet(() => pTypedDyn.Get("Name"))</li>
</ol>


<h4>Working with dictionaries</h4>
@{
  var dicDyn = lib.GetDictionary();
  var dic = dicDyn as System.Collections.Generic.Dictionary<string, string>;
}
<p>
  If the returned object is based on a dictionary, things are a bit easier.
  This is because <code>dynamic</code> will be able to work with it, but it's not type safe.
</p>
<ol>
  <li>dicDyn: @dicDyn</li>
  <li>dicDyn Type: @dicDyn.GetType()</li>
  <li>dicDyn Count ✅: @dicDyn.Count</li>
  <li>dicDyn["Name"] ✅: @dicDyn["Name"]</li>
  <li>dic: @dic</li>
  <li>dic Type: @dic.GetType()</li>
  <li>dic Count ✅: @dic.Count</li>
  <li>dic["Name"] ✅: @dic["Name"]</li>
</ol>

Source Code of ReturnObjects.cs

using System.Collections.Generic;
using ToSic.Sxc.Data;

/// <summary>
/// Demonstrates how to return objects, for tutorials which show how to use them.
/// </summary>
public class ReturnObjects: Custom.Hybrid.CodeTyped {
  public object GetAnonymous() {
    return new {
      Name = "John",
      Age = 30
    };
  }

  public ITyped GetTyped() {
    return AsTyped(new {
      Name = "John",
      Age = 30
    });
  }

  public Person GetPerson() {
    return new Person() {
      Name = "John",
      Age = 30
    };
  }

  public Dictionary<string, string> GetDictionary() {
    return new Dictionary<string, string>() {
      { "Name", "John" },
      { "Age", "30" }
    };
  }

  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }  
}
  1. Reuse Shared Razor Helpers (CreateInstance / GetCode)

    Use a shared razor file to hold multiple functions / helpers, and call them one-by-one as needed. 

Work with Content Items - MyItem

MyItem is the object which contains the first thing added by the editor on the current block.

Every view/template receives prepared data, usually on the MyItem object.

⬇️ Result | Source ➡️

Douglas Adams on MyItem

Id, Guid and Title are built-in properties

  1. Id: 46284
  2. Guid: 36726e4e-21cd-4c00-9ce8-72080f8935da
  3. Title: Douglas

Use Get(…) if you don't care about the var type...

  1. Name: Douglas
  2. Birthday: 3/11/1952 12:00:00 AM

... or Get<T>(…) which will try to treat as the expected type.

  1. Birthday Get<string> 03/11/1952 00:00:00
  2. Birthday Get<DateTime> 3/11/1952 12:00:00 AM
  3. Birthday Get<bool> False
  4. Birthday Get<int> 0

Use typed methods such as .String(…) if you care about the variable type.

  1. Name (strings): Douglas Adams
  2. Birthday: 3/11/1952
  3. Is Alive: False
  4. Fav Num. Int: 42
  5. Fav Num. Float: 41.99

Use fallback: … to handle empty values or conversion problems.

  1. Name (strings): Douglas
  2. Name (int): 12345

Use .Attribute(…) to safely encode properties. Mouse over this to see the effect.

@inherits Custom.Hybrid.RazorTyped

<h4>Douglas Adams on <code>MyItem</code></h4>
<p>
  Id, Guid and Title are built-in properties
</p>
<ol>
  <li>Id:             @MyItem.Id</li>
  <li>Guid:           @MyItem.Guid</li>
  <li>Title:          @MyItem.Title</li>
</ol>

<p>Use <code>Get(…)</code> if you don't care about the var type...</p>
<ol>
  <li>Name:           @MyItem.Get("FirstName")</li>
  <li>Birthday:       @MyItem.Get("Birthday")</li>
</ol>

<p>... or <code>Get&lt;T&gt;(…)</code> which will try to treat as the expected type.</p>
<ol>
  <li>Birthday Get&lt;string&gt;    @(MyItem.Get<string>("Birthday"))</li>
  <li>Birthday Get&lt;DateTime&gt;  @(MyItem.Get<DateTime>("Birthday"))</li>
  <li>Birthday Get&lt;bool&gt;      @(MyItem.Get<bool>("Birthday"))</li>
  <li>Birthday Get&lt;int&gt;       @(MyItem.Get<int>("Birthday"))</li>
</ol>

<p>Use typed methods such as <code>.String(…)</code> if you care about the variable type.</p>
<ol>
  @* use .String(…), .DateTime(…), .Int(…) to make it typed *@
  <li>Name (strings): @MyItem.String("FirstName") @MyItem.String("LastName")</li>
  <li>Birthday:       @MyItem.DateTime("Birthday").ToString("d")</li>
  <li>Is Alive:       @MyItem.Bool("IsAlive")</li>

  @* use .Int(…), .Long(…), .Float(…) etc. for numbers *@
  <li>Fav Num. Int:   @MyItem.Int("FavoriteNumber")</li>
  <li>Fav Num. Float: @MyItem.Float("FavoriteNumber")</li>
</ol>

<p>Use <code>fallback: …</code> to handle empty values or conversion problems.</p>
<ol>
  @* this has no effect, as the value works *@
  <li>Name (strings): @MyItem.String("FirstName", fallback: "unknown")
  <li>Name (int):     @MyItem.Int("FirstName", fallback: 12345)
</ol>


<p title='@MyItem.Attribute("FirstName")'>
  Use <code>.Attribute(…)</code> to safely encode properties.
  Mouse over this to see the effect.
</p>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)
  1. Working with Entity (Item) Values

    Every thing is an Entity. Here some basic examples how to show values like Name, Birthday etc. of such an Entity.

  2. Working with Block Contents

    Show content which was entered for this module

⬇️ Result | Source ➡️

Douglas Adams on MyItem

Note: The Biography contains a LOT of HTML...

Teaser (using .Raw() for Umlauts):
Douglas Noël Adams  (11 March 1952 – 11 May 2001) was an English author,  humorist , and…


Now as rich HTML
(drag size to see responsiv behavior)

Douglas Noël Adams (11 March 1952 – 11 May 2001) was an English author, humorist, and screenwriter, best known for The Hitchhiker's Guide to the Galaxy. Originally a 1978 BBC radio comedyThe Hitchhiker's Guide to the Galaxy developed into a "trilogy" of five books that sold more than 15 million copies in his lifetime. 

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

<h4>Douglas Adams on <code>MyItem</code></h4>
<p>
  Note: The Biography contains a LOT of HTML...
</p>
@{
  // scrubHtml will clean up Html tags; there are more options
  var bio = MyItem.String("Biography", scrubHtml: true);

  // Text.Ellipsis uses RazorBlade
  var teaser = Text.Ellipsis(bio, 100);
}
<p>
  <strong>Teaser</strong> (using .Raw() for Umlauts): <br>
  <em>@Html.Raw(teaser)</em>
</p>
<hr>
<div>
  <strong>Now as rich HTML</strong> <br>
  (drag size to see responsiv behavior) <br>
  @MyItem.Html("Biography")
</div>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)

The Presentation property only exists if the view is configured to use Presentation. In which case this is used to specify additional information how something should look or be presented. 

⬇️ Result | Source ➡️

A Person on MyItem

Note that the following should be green (probably green)

Content number 1

@inherits Custom.Hybrid.RazorTyped

<h4>A Person on <code>MyItem</code></h4>
@{
  var itemPres = MyItem.Presentation;
}

<p>
  Note that the following should be @itemPres.String("Color") (probably green)
</p>
<p style='color: @itemPres.Attribute("Color")'>
  @MyItem.Title
</p>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: QuickRefContent
  • Content/Item IsList: True
  • Content/Item Data:
    1. Content number 1 (ID: 46806) - Presentation: green (ID: 46802)
    2. Content two (ID: 46807) - Presentation: red (ID: 46809)
    3. Content three (ID: 46811) - Presentation: cyan (ID: 46810)
  1. Working with Entity (Item) Values

    Every thing is an Entity. Here some basic examples how to show values like Name, Birthday etc. of such an Entity.

  2. Working with Block Contents

    Show content which was entered for this module

Child items such as Awards are accessed using Child("Awards"). There are many ways to work with them.

⬇️ Result | Source ➡️

Use .Child("Awards") to get one

  1. Award ID: 46329
  2. Award Name: Hugo Award
  3. Award Name (Path): Hugo Award

Use ContainsKey(…), IsEmpty(…), IsNotEmpty(…)

  1. ContainsKey("Awards"): True
  2. ContainsKey("Awards2"): False
  3. IsEmpty("Awards"): False
  4. IsNotEmpty("Awards"): True
  5. IsEmpty("Awards.Name"): False
  6. IsEmpty("Awards2.Name"): True
  7. IsEmpty("Awards.NameX"): True

Use .Children("Awards") to get all

Award Count: 2
  1. Award: Hugo Award
  2. Award: Inkpot Award
@inherits Custom.Hybrid.RazorTyped

</hide-silent>
<h4>Use <code>.Child("Awards")</code> to get one</h4>
<ol>
  <li>Award ID:          @MyItem.Child("Awards").Id</li>
  <li>Award Name:        @MyItem.Child("Awards").String("Name")</li>
  <li>Award Name (Path): @MyItem.String("Awards.Name")</li>
</ol>

<p>Use ContainsKey(…), IsEmpty(…), IsNotEmpty(…)</p>
<ol>
  <li>ContainsKey("Awards"):  @MyItem.ContainsKey("Awards")</li>
  <li>ContainsKey("Awards2"): @MyItem.ContainsKey("Awards2")</li>
  <li>IsEmpty("Awards"):      @MyItem.IsEmpty("Awards")</li>
  <li>IsNotEmpty("Awards"):   @MyItem.IsNotEmpty("Awards")</li>
  <li>IsEmpty("Awards.Name"): @MyItem.IsEmpty("Awards.Name")</li>
  <li>IsEmpty("Awards2.Name"): @MyItem.IsEmpty("Awards2.Name")</li>
  <li>IsEmpty("Awards.NameX"): @MyItem.IsEmpty("Awards.NameX")</li>
</ol>

<h4>Use <code>.Children("Awards")</code> to get all</h4>
<span>Award Count:       @MyItem.Children("Awards").Count()</span>
<ol>
  @foreach (var award in MyItem.Children("Awards")) {
    <li>Award: @award.String("Name")</li>
  }
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)
  1. Working with Entity (Item) Values

    Every thing is an Entity. Here some basic examples how to show values like Name, Birthday etc. of such an Entity.

  2. Working with Block Contents

    Show content which was entered for this module

Properties can contain urls such as /abc.jpg or file references like file:72.

⬇️ Result | Source ➡️

Douglas Adams, the current item (MyItem)

Use .Url(…) to resolve file references such as file:72

  1. Mugshot field Value: file:137
  2. Mugshot URL: /razortutorial12/app/Tutorial-Razor/adam/Tm5yNs0hAEyc6HIID4k12g/Mugshot/douglas%20adams.png
  3. Mugshot Picture
@inherits Custom.Hybrid.RazorTyped

<h4>Douglas Adams, the current item (MyItem)</h4>
<p>Use <code>.Url(…)</code> to resolve file references such as <code>file:72</code></p>
<ol>
  <li>Mugshot field Value:  @MyItem.String("Mugshot")</li>
  <li>Mugshot URL:          @MyItem.Url("Mugshot")</li>
  <li>
    Mugshot Picture <br>
    @MyItem.Picture("Mugshot", settings: "Square", width: 100, imgClass: "rounded-circle")
  </li>
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)

Properties can contain urls such as /abc.jpg or file references like file:72.

⬇️ Result | Source ➡️

Douglas Adams, the current item (MyItem)

  1. File name: douglas adams
  2. File extension: png
  3. Size (bytes): 39125
  4. SizeInfo: 38.2 KB

Every file-field is actually a folder...

  1. Files count: 2
  2. Sub-Folders: 0

...which could hold many files. If you want to show them, you need Kit.Image...

  1. douglas adams
  2. not-panicking
@inherits Custom.Hybrid.RazorTyped

<h4>Douglas Adams, the current item (MyItem)</h4>
@{
  var file = MyItem.File("Mugshot");
  var sizeInfo = file.SizeInfo;
}
<ol>
  <li>File name:        @file.Name</li>
  <li>File extension:   @file.Extension</li>
  <li>Size (bytes):     @file.Size</li>
  <li>SizeInfo:         @sizeInfo.BestSize.ToString("#.#") @sizeInfo.BestUnit</li>
</ol>

<p>Every file-field is actually a folder...</p>
@{
  var folder = MyItem.Folder("Mugshot");
}
<ol>
  <li>Files count:    @folder.Files.Count()</li>
  <li>Sub-Folders:    @folder.Folders.Count()</li>
</ol>
<p>
  ...which could hold many files. 
  If you want to show them, you need <code>Kit.Image...</code></p>
<ol>
  @foreach (var f in folder.Files) {
    <li>@f.Name <br> @Kit.Image.Picture(f, width: 100)</li>
  }
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)

⬇️ Result | Source ➡️

Inspect the fields of an Item

  1. ✅ IsAlive: False
  2. 🔲 Sex:
  3. ✅ Birthday: 3/11/1952 12:00:00 AM
  4. ✅ Awards: System.Collections.Generic.List`1[ToSic.Sxc.Data.ITypedItem]
  5. ✅ LastName: Adams
  6. 🔲 Haters: System.Collections.Generic.List`1[ToSic.Sxc.Data.ITypedItem]
  7. ✅ Mugshot: file:137
  8. ✅ FirstName: Douglas
  9. ✅ FavoriteNumber: 41.99
  10. ✅ Biography: <p><strong><img class="wysiwyg-right wysiwyg-50" src="/Portals/tutorial-razor/adam/Tutorial-Razor/Tm5yNs0hAEyc6HIID4k12g/Biography/douglas_adams_portrait.jpg" data-cmsid="file:douglas_adams_portrait.jpg">Douglas No&euml;l Adams</strong>&nbsp;(11 March 1952 &ndash; 11 May 2001) was an English author,&nbsp;<a title="Humorist" href="https://en.wikipedia.org/wiki/Humorist" target="_blank" rel="noopener">humorist</a>, and screenwriter, best known for&nbsp;<i><a title="The Hitchhiker's Guide to the Galaxy" href="https://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy" target="_blank" rel="noopener">The Hitchhiker's Guide to the Galaxy</a></i>. Originally a 1978&nbsp;<a title="The Hitchhiker's Guide to the Galaxy (radio series)" href="https://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy_(radio_series)" target="_blank" rel="noopener">BBC radio comedy</a>,&nbsp;<i>The Hitchhiker's Guide to the Galaxy</i> developed into a "trilogy" of five books that sold more than 15&nbsp;million copies in his lifetime.&nbsp;<sup id="cite_ref-radioacad_2-0" class="reference"></sup></p>

Let's do some manual inspection

  1. ✅ "FirstName" exits?
  2. 🔲 "FirstName" is empty?
  3. ✅ "FirstName" is not empty?
  4. 🔲 "hello" exits?
  5. ✅ "hello" is empty?
  6. 🔲 "hello" is not empty?
@inherits Custom.Hybrid.RazorTyped

@functions {
  // quick helper to make the output nicer
  string Boolmoji(bool value) { return value ? "✅" : "🔲"; }
}

<h4>Inspect the fields of an Item</h4>
<ol>
  @foreach (var key in MyItem.Keys()) {
    <li>
      @Boolmoji(MyItem.IsNotEmpty(key))
      @key:
      @MyItem.Get(key)
    </li>
  }
</ol>

<h4>Let's do some manual inspection</h4>
<ol>
  <li>@Boolmoji(MyItem.ContainsKey("FirstName")) "FirstName" exits?</li>
  <li>@Boolmoji(MyItem.IsEmpty("FirstName")) "FirstName" is empty?</li>
  <li>@Boolmoji(MyItem.IsNotEmpty("FirstName")) "FirstName" is not empty?</li>
  <li>@Boolmoji(MyItem.ContainsKey("hello")) "hello" exits?</li>
  <li>@Boolmoji(MyItem.IsEmpty("hello")) "hello" is empty?</li>
  <li>@Boolmoji(MyItem.IsNotEmpty("hello")) "hello" is not empty?</li>
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item Data:
    1. Douglas (ID: 46284)

In some scenarios you need may expect that data isn't there. In that case, you can create fake items to use in your razor.

⬇️ Result | Source ➡️

Inspect the fields of an Item

  1. ✅ FirstName: John
  2. ✅ LastName: Doe
@inherits Custom.Hybrid.RazorTyped

@{
  // Create a mock item, to be used if nothing else is found
  var mockPerson = AsItem(new {
    FirstName = "John",
    LastName = "Doe"
  }, mock: true); // mock: true is required to be sure you wanted this

  // Let's pretend the query may have a stream or may not
  var stream = MyData.GetStream("Guys", emptyIfNotFound: true);

  // Get the item, or the mock if nothing is found
  var guy = AsItem(stream) ?? mockPerson;
}
<h4>Inspect the fields of an Item</h4>
<ol>
  @foreach (var key in guy.Keys()) {
    <li>
      @(guy.IsNotEmpty(key) ? "✅" : "🔲")
      @key:
      @guy.Get(key)
    </li>
  }
</ol>

Work with List of Items - MyItems

Every view/template receives prepared data, either entered by the user on this page, or provided through a query. It can contain many items - so to get the list, use MyItems

⬇️ Result | Source ➡️

Loop persons which were added to this view

  • Douglas Adams
  • Terry Pratchett
  • Neil Gaiman
@inherits Custom.Hybrid.RazorTyped

<h4>Loop persons which were added to this view</h4>
<ul>
  @foreach (var person in MyItems) {
    <li>
      @person.Picture("Mugshot", settings: "Square", width: 50, imgClass: "rounded-circle")
      @person.String("FirstName") @person.String("LastName")
    </li>
  }
</ul>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: Persons
  • Content/Item IsList: True
  • Content/Item Data:
    1. Douglas (ID: 46284)
    2. Terry (ID: 46285)
    3. Neil (ID: 46286)

⬇️ Result | Source ➡️

  • Content number 1
    FYI: Heading none

    We are one!

  • Content two

    FYI: Heading h5

    Two be or !2B

  • Content three 🌟

    FYI: Heading h6

    Three's the charm

@inherits Custom.Hybrid.RazorTyped

<ul>
  @foreach (var item in MyItems) {
    var pres = item.Presentation;
    
    var hType = pres.String("HeadingType");
    var title = item.Title + (pres.Bool("Highlight") ? " 🌟" : "");
    <li>
      @* Create a heading tag the size specified in Presentation *@
      @if (pres.IsNotEmpty("HeadingType")) {
        @Kit.HtmlTags.Custom(hType).Wrap(title)
      } else {
        @title
      }
      <br>
      <em>FYI: Heading @(pres.IsEmpty("HeadingType") ? "none" : pres.String("HeadingType"))</em>
      <div style='color: @pres.Attribute("Color")'>
        @item.Html("Contents")
      </div>
    </li>
  }
</ul>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: QuickRefContent
  • Content/Item IsList: True
  • Content/Item Data:
    1. Content number 1 (ID: 46806) - Presentation: green (ID: 46802)
    2. Content two (ID: 46807) - Presentation: red (ID: 46809)
    3. Content three (ID: 46811) - Presentation: cyan (ID: 46810)

⬇️ Result | Source ➡️

Check out this list

  • Content number 1
  • Content two
  • Content three
@inherits Custom.Hybrid.RazorTyped

<h3>@MyHeader.Title</h3>
<ul>
  @foreach (var item in MyItems) {
    <li>
      @item.Title
    </li>
  }
</ul>

View Configuration

This is how this view would be configured for this sample.

  • Content/Item ContentType: QuickRefContent
  • Content/Item IsList: True
  • Content/Item Data:
    1. Content number 1 (ID: 46806)
    2. Content two (ID: 46807)
    3. Content three (ID: 46811)
  • Header Type: QuickRefContentHeader
  • Header Item: Check out this list (ID: 46805)

Data and Query - MyDataApp.GetQuery(...) and AsItems(...)

Every view/template receives prepared data, either entered by the user on this page, or provided through a query.

⬇️ Result | Source ➡️

Loop persons in Query of this view

  • Douglas Adams
  • Terry Pratchett
  • Neil Gaiman
@inherits Custom.Hybrid.RazorTyped

<h4>Loop persons in Query of this view</h4>
<ul>
  @foreach (var person in AsItems(MyData)) {
    <li>
      @person.Picture("Mugshot", settings: "Square", width: 50, imgClass: "rounded-circle")
      @person.String("FirstName") @person.String("LastName")
    </li>
  }
</ul>

View Configuration

This is how this view would be configured for this sample.

  • Query: QuickRef-Persons-Selected
Details for Query: QuickRef-Persons-Selected

This query will retrieve Terry Pratchett on one stream, and 3 other persons an another stream.

This uses a query which returns a list of Persons and a selected person separately. The Default stream has persons, and Selected has a selected person to compare with. Compare using .Equals(…).

⬇️ Result | Source ➡️

Selected: Terry Pratchett
  • Douglas Adams
  • Terry Pratchett (selected ⭐)
  • Neil Gaiman
@inherits Custom.Hybrid.RazorTyped

@{
  var selected = AsItem(MyData.GetStream("Selected"));
}
<div>
  Selected: @selected.String("FirstName") @selected.String("LastName")
</div>
<ul>
  @foreach (var person in AsItems(MyData)) {
    <li>
      @person.String("FirstName") @person.String("LastName")
      @if (person.Equals(selected)) {
        <strong>(selected ⭐)</strong>
      }
    </li>
  }
</ul>

View Configuration

This is how this view would be configured for this sample.

  • Query: QuickRef-Persons-Selected
Details for Query: QuickRef-Persons-Selected

This query will retrieve Terry Pratchett on one stream, and 3 other persons an another stream.

⬇️ Result | Source ➡️

Get Books from the Default stream

  1. Hitchhikers Guide to the Galaxy
  2. Good Omens
  3. Phishing for Phools
  4. The Last Continent

Get Authors from the Authors stream

  1. Douglas
  2. Terry
  3. Neil
  4. George
  5. Raphael
  6. Ed
@inherits Custom.Hybrid.RazorTyped

@{
  // Get a query to be executed when accessed
  // It has 2 streams: Default and Authors
  var query = App.GetQuery("QuickRef-GetQuery");
}
<h4>Get Books from the <code>Default</code> stream</h4>
<ol>
  @foreach (var book in AsItems(query)) {
    <li>@book.Title</li>
  }
</ol>

<h4>Get Authors from the <code>Authors</code> stream</h4>
<ol>
  @foreach (var author in AsItems(query.GetStream("Authors"))) {
    <li>@author.Title</li>
  }
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Query: QuickRef-GetQuery
Details for Query: QuickRef-GetQuery

⬇️ Result | Source ➡️

Get Books from the Default stream

  1. Hitchhikers Guide to the Galaxy
  2. Good Omens
  3. Phishing for Phools

Get Authors from the Authors stream

  1. Douglas
  2. Terry
  3. Neil
@inherits Custom.Hybrid.RazorTyped

@{
  // Get a query to be executed when accessed
  // It has 2 streams: Default and Authors
  // Since the query expects a `Max` parameter, we pass it in
  // ...it will then only return 3 items on each stream
  var query = App.GetQuery("QuickRef-GetQuery", parameters: new {
    Max = 3
  });
}
<h4>Get Books from the <code>Default</code> stream</h4>
<ol>
  @foreach (var book in AsItems(query)) {
    <li>@book.Title</li>
  }
</ol>

<h4>Get Authors from the <code>Authors</code> stream</h4>
<ol>
  @foreach (var author in AsItems(query.GetStream("Authors"))) {
    <li>@author.Title</li>
  }
</ol>

View Configuration

This is how this view would be configured for this sample.

  • Query: QuickRef-GetQuery
Details for Query: QuickRef-GetQuery

⬇️ Result | Source ➡️

Let's see the if anything exists

  1. ✅ Authors exists?
  2. 🔲 Critics exists?
  3. ✅ Players exists?
  4. ✅ Critics || Authors exists?
  5. ✅ Players || Authors exists?

Now let's count what's inside

  1. Authors: 6
  2. Critics: skip
  3. Players: 0
  4. Critics || Authors: 6
  5. Players || Authors: 0
@inherits Custom.Hybrid.RazorTyped

@{
  // Get a query to be executed when accessed
  // It has 2 streams: Default and Authors
  var query = App.GetQuery("QuickRef-GetQuery");

  // Get the stream Authors (this exists)
  var authors = query.GetStream("Authors");

  // Get the stream Critics (this does not exist)
  // we'll take a null instead of an error
  var critics = query.GetStream("Critics", nullIfNotFound: true);

  // This will get the authors, as critics is null
  var cOrA = critics ?? authors;

  // Get the stream players (this does not exist)
  // we want an empty list instead of an error
  var players = query.GetStream("Critics", emptyIfNotFound: true);

  // This will keep the empty list, as it's not null
  var pOrA = players ?? authors;
}
<h4>Let's see the if anything exists</h4>
<ol>
  <li>@(authors != null ? "✅" : "🔲") Authors exists? </li>
  <li>@(critics != null ? "✅" : "🔲") Critics exists? </li>
  <li>@(players != null ? "✅" : "🔲") Players exists? </li>
  <li>@(cOrA != null ? "✅" : "🔲") Critics || Authors exists? </li>
  <li>@(pOrA != null ? "✅" : "🔲") Players || Authors exists? </li>
</ol>

<h4>Now let's count what's inside</h4>
<ol>
  <li>Authors: @authors.Count()</li>
  <li>Critics: <em>skip</em></li>
  <li>Players: @players.Count()</li>
  <li>Critics || Authors: @cOrA.Count()</li>
  <li>Players || Authors: @pOrA.Count()</li>
</ol>

@* TODO:: Query Img *@

View Configuration

This is how this view would be configured for this sample.

  • Query: QuickRef-GetQuery
Details for Query: QuickRef-GetQuery

Work with Settings and Resources

Settings can be configured at many levels, the most-local (eg. the App) would override the most global (eg. Presets).

⬇️ Result | Source ➡️

Get some App Setting / Resources

  1. Setting CustomColor:
    #E91E63F2
  2. Setting CsvFile:
    file:189
    /razortutorial12/app/Tutorial-Razor/adam/FP6nrGBJZUKPRnldQOMQEQ/CsvFile/products.csv
  3. Resource ButtonOrder:

Get Global Settings

  1. Settings.Images.Content.Width
    1400
  2. The same with prefix Settings
    1400
  3. GoogleMaps.DefaultCoordinates
    {"latitude":47.1747,"longitude":9.4692}
  4. GoogleMaps.DefaultCoordinates as Typed
    Lat: 47.1747
    Lng: 9.4692
@inherits Custom.Hybrid.RazorTyped

<h4>Get some App Setting / Resources</h4>
<ol>
  <li>Setting CustomColor: <br>
    <code>@App.Settings.String("CustomColor")</code>
  </li>
  <li>Setting CsvFile: <br>
    <code>@App.Settings.String("CsvFile")</code> <br>
    <code>@App.Settings.Url("CsvFile")</code>
  </li>
  <li>Resource ButtonOrder: <br>
    <button type="button">@App.Resources.String("ButtonOrder")</button>
  </li>
</ol>

<h4>Get <em>Global</em> Settings</h4>
<ol>
  <li>Settings.Images.Content.Width <br>
    <code>@AllSettings.Int("Images.Content.Width")</code>
  </li>
  <li>The same with prefix <code>Settings</code> <br>
    <code>@AllSettings.Int("Settings.Images.Content.Width")</code>
  </li>
  <li>GoogleMaps.DefaultCoordinates <br>
    <code>@AllSettings.Get("Settings.GoogleMaps.DefaultCoordinates")</code>
  </li>
  <li>GoogleMaps.DefaultCoordinates as Typed <br>
    @{
      var json = AllSettings.String("Settings.GoogleMaps.DefaultCoordinates");
      var coords = Kit.Json.ToTyped(json);
    }
    Lat: @coords.Float("Latitude") <br>
    Lng: @coords.Float("Longitude")
  </li>
</ol>
  1. Settings in 2sxc
    Settings allow your code to use predefined settings across all apps and sites. And they allow you to overide a setting at any level of the application. You can read more about the settings in the docs 📕 .

Page, Site, Platform and More

Set Page Title, Keywords etc.

⬇️ Result | Source ➡️

Note: you won't see any output here. To see the effect, look at the page source.

@inherits Custom.Hybrid.RazorTyped

<p>
  Note: you won't see any output here.
  To see the effect, look at the page source.
</p>
@* Example with a single-liner directly in the code *@
@Kit.Page.SetTitle("Reference CheatSheet for Razor in 2sxc")
@Kit.Page.AddIconSet(App.Folder.Url + "/blade/assets/razor-blade-icon.png")
@Kit.Page.AddMeta("tutorial", "some value")

@{
  // Example showing how to use in a block of code
  Kit.Page.SetDescription("Learn to use Razor Blade ");
  Kit.Page.SetKeywords("Tutorial, Razor, Blade");
}

Output is Invisible as it only affects the HTML Head

This sample modifies the HTML head, so it's not visible here. 

To see the effect, look at the source in the browser

  1. Set Page Title, Keywords, Descriptions etc.

    Get/Set Page Title, Keywords, Description and set meta-tags and more.

  2. Set <base> tag in header

    Add a <base> tag to the header. This is important for SPA JS applications.

  3. Page Icons for Favicon, Apple/Android

    Add various combinations of icons to the page header

  4. Meta and other Tags in header

    Add all kinds of meta-tags to the header. 

Set OpenGraph Headers etc.

⬇️ Result | Source ➡️

This uses Kit.Page.SetTitle(...) and other methods to modify the HTML sent to the browser. It sets various aspecs such as title or FavIcons.

  1. meta title, meta description, meta keywords
  2. favicon
  3. some JsonLd for google
  4. OpenGraph headers for FaceBook, Twitter, etc.
@inherits Custom.Hybrid.Razor14

@* Create a JSON-LD using an object - replicating googles example https://developers.google.com/search/docs/guides/intro-structured-data *@
@Kit.Page.AddJsonLd(new Dictionary<string, object> {
    { "@context", "https://schema.org"},
    { "@type", "Organization"},
    { "url", "http://www.example.com"},
    { "name", "Unlimited Ball Bearings Corp."},
    { "contactPoint", new Dictionary<string, object> {
      {"@type", "ContactPoint"},
      {"telephone", "+1-401-555-1212"},
      {"contactType", "Customer service"}
    }}
})

@* Set some OpenGraph headers *@
@Kit.Page.AddOpenGraph("title", "2sxc QuickRef Razor Typed")
@Kit.Page.AddOpenGraph("type", "website")
<p>
  This uses <code>Kit.Page.SetTitle(...)</code> and other methods to modify the HTML sent to the browser. 
  It sets various aspecs such as <code>title</code> or FavIcons.
</p>
<ol>
  <li><code>meta title</code>, <code>meta description</code>, <code>meta keywords</code> </li>
  <li>favicon</li>
  <li>some JsonLd for google</li>
  <li>OpenGraph headers for FaceBook, Twitter, etc.</li>
</ol>

Output is Invisible as it only affects the HTML Head

This sample modifies the HTML head, so it's not visible here. 

To see the effect, look at the source in the browser

  1. Set Open-Graph headers for Social Media using the IPageService

    Add Open-Graph data headers for Facebook, Twitter and other sharing-systems

  2. JSON-LD Headers for SEO

    Add JSON-LD (Linked Data) headers for Google

Get Info about Platform, Culture/Languages etc.

⬇️ Result | Source ➡️

  1. Platform name: 'Oqtane'
  2. Platform type: 'Oqtane'
  3. Platform version: '5.0.2'
  4. Culture Current Code: 'en-us'
  5. Culture Default Code: 'en-us'
  6. Site Id: '13'
  7. Site Url: 'https://oqt-apps-dev.2sxc.org/razortutorial12'
  8. Site UrlRoot: 'oqt-apps-dev.2sxc.org/razortutorial12'
  9. Page Id: '288'
  10. Page Url: 'https://oqt-apps-dev.2sxc.org/razortutorial12'
  11. Page Url Parameters: 'id=46290&tut=quickref'
  12. Module Id: '352'
  13. User Id: '-1'
  14. User Name: ''
  15. User IsContentAdmin: 'False'
  16. User IsSiteAdmin: 'False'
  17. User IsSystemAdmin: 'False'
  18. View Id: '47382'
  19. View Identifier: '' (blank if no special identifier)
  20. View Name: 'Default Tutorial Page (new v16)'
  21. View Edition: '' (blank if no special CSS Framework edition)
  22. Unique Key '-j2WALCV'
@inherits Custom.Hybrid.RazorTyped

<ol>
  <li>Platform name: '<code>@MyContext.Platform.Name</code>'</li>
  <li>Platform type: '<code>@MyContext.Platform.Type</code>'</li>
  <li>Platform version: '<code>@MyContext.Platform.Version</code>'</li>

  <li>Culture Current Code: '<code>@MyContext.Culture.CurrentCode</code>'</li>
  <li>Culture Default Code: '<code>@MyContext.Culture.DefaultCode</code>'</li>

  <li>Site Id: '<code>@MyContext.Site.Id</code>'</li>
  <li>Site Url: '<code>@MyContext.Site.Url</code>'</li>
  <li>Site UrlRoot: '<code>@MyContext.Site.UrlRoot</code>'</li>

  <li>Page Id: '<code>@MyPage.Id</code>'</li>
  <li>Page Url: '<code>@MyPage.Url</code>'</li>
  <li>Page Url Parameters: '<code>@MyPage.Parameters</code>'</li>

  <li>Module Id: '<code>@MyContext.Module.Id</code>'</li>

  <li>User Id: '<code>@MyUser.Id</code>'</li>
  <li>User Name: '<code>@MyUser.Name</code>'</li>
  <li>User IsContentAdmin: '<code>@MyUser.IsContentAdmin</code>'</li>
  <li>User IsSiteAdmin: '<code>@MyUser.IsSiteAdmin</code>'</li>
  <li>User IsSystemAdmin: '<code>@MyUser.IsSystemAdmin</code>'</li>

  <li>View Id: '<code>@MyView.Id</code>'</li>
  <li>View Identifier: '<code>@MyView.Identifier</code>' (blank if no special identifier) </li>
  <li>View Name: '<code>@MyView.Name</code>'</li>
  <li>View Edition: '<code>@MyView.Edition</code>' (blank if no special CSS Framework edition) </li>

  <li>Unique Key '<code>@UniqueKey</code>'</li>
</ol>
  1. Work with the Context (Page, Site, etc.)
    Whenever you need to know about the current environment, you need  Context information. These are the contexts you should know about: Platform Site Page Module View User

JavaScript - Working with Razor and WebApi

Loading JavaScripts is standard HTML using <script src="..."< tags. What more challenging is getting the right path to scripts in this app or relative to the current Razor file.

⬇️ Result | Source ➡️

Load a JavaScript relative to the App folder (see message/console):

Load a JavaScript relative to the folder of this Razor file (see message/console):

@inherits Custom.Hybrid.RazorTyped

<p>
  Load a JavaScript relative to the App folder (see message/console): <br>
  <strong id="loadjs-app-message"></strong>
</p>
<script src="@App.Folder.Url/js/quickref/js/load-with-app-path.js"></script>

<p>
  Load a JavaScript relative to the folder of this Razor file (see message/console): <br>
  <strong id="loadjs-view-message"></strong>
</p>

<script src="@MyView.Folder.Url/quickref/js/load-with-view-path.js"></script>

Source Code of load-with-app-path.js

// This will simply show a message in the console and DOM when the script is loaded
// Note that it will automatically run when the script is loaded
// This is not a good practice, but it is useful for this demo

console.log('The script for the Quick Reference was loaded - relative to the the @App.Folder.Url.');

document.getElementById('loadjs-app-message').innerHTML = 
  'Hello from report-loaded-in-console-path-app.js! This was loaded using @App.Folder.Url.';

Source Code of load-with-view-path.js

// This will simply show a message in the console and DOM when the script is loaded
// Note that it will automatically run when the script is loaded
// This is not a good practice, but it is useful for this demo

console.log('The script for the Quick Reference was loaded - relative to the the @MyView.Folder.Url.');

document.getElementById('loadjs-view-message').innerHTML = 
  'Hello from report-loaded-in-console-path-app.js! This was loaded using @MyView.Folder.Url.';

A core challenge is to activate JavaScripts only when needed - and when all dependencies are loaded. This is what turnOn does. It checks if all specified JS objects exist, and then triggers the JS code.

⬇️ Result | Source ➡️

The following text will be replaced once the JS is triggered:

This example passes data to the JS, so it can be parameterized:

We can also pass in more sophisticated data:

@inherits Custom.Hybrid.RazorTyped

<p>
  The following text will be replaced once the JS is triggered: <br>
  <strong id="turnOn1-message"></strong>
</p>

@* Load the JavaScript - it will not run by itself, as the DOM might not be ready
    Note that this can be before or after the TurnOn *@
<script src="@App.Folder.Url/js/quickref/js/turnOn1.js"></script>

@* Tell TurnOn to trigger the JS when everything is ready *@
@Kit.Page.TurnOn("window.quickReference.turnOn1Message()")

<p>
  This example passes data to the JS, so it can be parameterized:  <br>
  <strong id="turnOn2-message"></strong>
</p>

@* Tell TurnOn to trigger the JS and give it a string *@
@Kit.Page.TurnOn("window.quickReference.turnOn2Message()", data: "Hello from Razor, the page Id is" + MyPage.Id)

<p>
  We can also pass in more sophisticated data:  <br>
  <strong id="turnOn3-message"></strong>
</p>
@{
  // Create an anonymous object containing the data to send
  var turnOn3Data = new {
    domId = "turnOn3-message",
    message = "Hello from Razor...",
    pageId = MyPage.Id,
    pageUrl = MyPage.Url
  };
}
@* Tell TurnOn to trigger the JS and give it a complex object *@
@Kit.Page.TurnOn("window.quickReference.turnOn3Message()", data: turnOn3Data)

Source Code of turnOn1.js

// Make sure the object exists, which can contain various scripts for the Quick Reference
// This makes sure that if other scripts also create it, they won't interfere with each other
window.quickReference = window.quickReference || {};

window.quickReference.turnOn1Message = function () {
  // get div in html with id 'turnOn1-message' and show "Hello World!"
  const tag = document.getElementById('turnOn1-message')
  tag.innerHTML = 'Hello from turnOn1.js! This was activated using turnOn';
}

window.quickReference.turnOn2Message = function (message) {
  // get div in html with id 'turnOn1-message' and show "Hello World!"
  const tag = document.getElementById('turnOn2-message');
  tag.innerHTML = message;
}

window.quickReference.turnOn3Message = function (config) {
  // get div in html with id 'turnOn1-message' and show "Hello World!"
  const tag = document.getElementById(config.domId);
  tag.innerHTML = config.message + 
    '; PageId: ' + config.pageId + 
    "; Url: " + config.pageUrl;
}

Work with the new UniqueKey with JavaScript - new v16.04 🌟

The UniqueKey is a new feature in 2sxc 16.04 which allows you to create unique IDs for HTML elements. The value of UniqueKey is the same in all Razor and C# files for the same Content-Block.

⬇️ Result | Source ➡️

This separate Razor will create a div for the resulting message, based on the same UniqueKey which is -j2WALCV

(script didn't run yet - this elements id is demo-uniquekey-msg--j2WALCV)
@inherits Custom.Hybrid.RazorTyped

@{
  var buttonId = "demo-uniquekey-btn-" + UniqueKey;
  var messageId = "demo-uniquekey-msg-" + UniqueKey;
}
@* Show the button *@
<button type="button" id="@buttonId" class="btn btn-primary">
  Press to see JS find the button using UniqueKey: @buttonId
</button>

@* Create the DIV for the message - it's in another file, but shares the UniqueKey *@
@Html.Partial("./Uniquekey-Message.cshtml")

@* Trigger the script, and pass in the IDs it will need based on the UniqueKey *@
@Kit.Page.TurnOn("window.quickReference.demoUniqueQuey()", data: new { buttonId, messageId })
@* Load the script - this can be before or after the TurnOn *@
<script src="@App.Folder.Url/js/quickref/js/unique-key.js"></script>

Source Code of Uniquekey-Message.cshtml

@inherits Custom.Hybrid.RazorTyped

<p>
  This separate Razor will create a div for the resulting message, based on the same <code>UniqueKey</code> which is <code>@UniqueKey</code>
</p>
<code id="demo-uniquekey-msg-@UniqueKey">(script didn't run yet - this elements id is <code>demo-uniquekey-msg-@UniqueKey</code>)</code>

Source Code of unique-key.js

// Make sure the object exists, which can contain various scripts for the Quick Reference
// This makes sure that if other scripts also create it, they won't interfere with each other
window.quickReference = window.quickReference || {};

// Bind a click action to the button with the unique id
window.quickReference.demoUniqueQuey = function (ids) {
  document.getElementById(ids.buttonId)
    .addEventListener("click", function () {
      const msg = 'The button was pressed. ' +
      'This was only possible thanks to the unique id using UniqueKey. ' + 
      'The ID this time was: ' + ids.buttonId + '.';

      // alert(msg);

      document.getElementById(ids.messageId).innerHTML = msg;
  });
}

Sometimes you need a UniqueKey which also depends on other objects. For example, you may need to have a UniqueKey which also uses another value - or many. This is done using @Kit.Key.UniqueKeyWith(...).

⬇️ Result | Source ➡️

  1. Unique Key with 12345: -j2WALCV-n12345
  2. Unique Key with "hello": -j2WALCV-s-538311920
  3. Unique Key with "bad chars in id ! % / 👍🏽": -j2WALCV-s96529808
  4. Unique Key with "this is a long text and should be shortened": -j2WALCV-s-721150468
  5. Unique Key with 12345 and "hello": -j2WALCV-n12345-s-538311920
@inherits Custom.Hybrid.RazorTyped

<ol>
  <li>
    Unique Key with <code>12345</code>:
    <code>@Kit.Key.UniqueKeyWith(12345)</code>
  </li>
  <li>
    Unique Key with <code>"hello"</code>:
    <code>@Kit.Key.UniqueKeyWith("hello")</code>
  </li>
  <li>
    Unique Key with <code>"bad chars in id ! % / 👍🏽"</code>:
    <code>@Kit.Key.UniqueKeyWith("bad chars in id ! % / 👍🏽")</code>
  </li>
  <li>
    Unique Key with <code>"this is a long text and should be shortened"</code>:
    <code>@Kit.Key.UniqueKeyWith("this is a long text and should be shortened")</code>
  </li>
  <li>
    Unique Key with <code>12345</code> and <code>"hello"</code>:
    <code>@Kit.Key.UniqueKeyWith(12345, "hello")</code>
  </li>
</ol>

If the UniqueKey is based on known object types such as Entities, this works very well. For example, you may need to loop through a list of items, and each item needs a unique key.

⬇️ Result | Source ➡️

  1. Title: Hitchhikers Guide to the Galaxy - UniqueKey: -j2WALCV-eid7md5eRsH
  2. Title: Good Omens - UniqueKey: -j2WALCV-eidcAcM-LSz
  3. Title: Phishing for Phools - UniqueKey: -j2WALCV-eidlkSe34Oo
  4. Title: The Last Continent - UniqueKey: -j2WALCV-eid_EnbtOtU
@inherits Custom.Hybrid.RazorTyped

@{
  var books = AsItems(App.Data["Books"]);
}
<ol>
  @foreach (var book in books) {
    <li>
      Title: <code>@book.Title</code> - UniqueKey: <code>@Kit.Key.UniqueKeyWith(book)</code>
    </li>
  }
</ol>

JSON

JSON data can be difficult to work with, because you would need to use System.Text.Json which has a sophisticated API. Instead, we can convert it to ITyped and then use a very simple API.

⬇️ Result | Source ➡️

  1. .Get("name"): Dude
  2. .Get("NAME") (insensitive): Dude
@inherits Custom.Hybrid.RazorTyped

@{
  // Variables for the tests
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";

  // Simple JSON strings which could come from a file or web service
  var jDude = System.IO.File.ReadAllText(jsonRoot + "/dude.json");

  // Convert the JSON to ITyped
  var dude = Kit.Json.ToTyped(jDude);
}
<ol>
  <li>
    <code>.Get("name")</code>: @dude.Get("Name")
  </li>
  <li>
    <code>.Get("NAME")</code> (insensitive): @dude.Get("NAME")
  </li>
</ol>

Source Code of dude.json

{
  "name": "Dude",
  "age": 47
}

This example shows more advanced scenarios. It uses the previously created dude object.

⬇️ Result | Source ➡️

  1. .String("Name") (typed): Dude
  2. .Int("Age"): 47
  3. Add using Int(...): 147
  4. Add using String(...): 10047
@inherits Custom.Hybrid.RazorTyped

@{
  // Variables for the tests
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";

  // Simple JSON strings which could come from a file or web service
  var jDude = System.IO.File.ReadAllText(jsonRoot + "/dude.json");

  // Convert the JSON to ITyped
  var dude = Kit.Json.ToTyped(jDude);
}
<ol>
  <li>
    <code>.String("Name")</code> (typed): @dude.String("Name")
  </li>
  <li>
    <code>.Int("Age")</code>: @dude.Int("Age")
  </li>
  <li>
    Add using Int(...): <code>@(100 + dude.Int("Age"))</code>
  </li>
  <li>
    Add using String(...): <code>@(100 + dude.String("Age"))</code>
  </li>
</ol>

Source Code of dude.json

{
  "name": "Dude",
  "age": 47
}

By default, the object is strict, so you can only access fields which are present.

⬇️ Result | Source ➡️

  1. strict.String("Fake") (typed): ⚠️ Exception: String('Fake', ...) not found and 'strict' is true, meaning that an error is thrown. Either a) correct the name 'Fake'; b) use String("Fake", required: false); or c) or use AsItem(..., propsRequired: false) or similar (Parameter 'name')
  2. strict.String("Fake", required: false):
  3. strict.String("Fake", required: false, fallback: "undefined"): undefined
  4. strict.Int("Fake", required: false): 0
  5. strict.Int("Fake", required: false, fallback: -1): -1
@inherits Custom.Hybrid.RazorTyped


@{
  // Variables for the tests
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";

  // Simple JSON strings which could come from a file or web service
  var jDude = System.IO.File.ReadAllText(jsonRoot + "/dude.json");
  var strict = Kit.Json.ToTyped(jDude);
}
<ol>
  <li>
    <code>strict.String("Fake")</code> (typed):
    @* Note: TryGet is a tutorial-internal helper to show the error *@
    @TryGet(() => strict.String("Fake"))
  </li>
  <li>
    <code>strict.String("Fake", required: false)</code>:
    @strict.String("Fake", required: false)
  </li>
  <li>
    <code>strict.String("Fake", required: false, fallback: "undefined")</code>:
    @strict.String("Fake", required: false, fallback: "undefined")
  </li>
  <li>
    <code>strict.Int("Fake", required: false)</code>:
    @strict.Int("Fake", required: false)
  </li>
  <li>
    <code>strict.Int("Fake", required: false, fallback: -1)</code>:
    @strict.Int("Fake", required: false, fallback: -1)
  </li>
</ol>

Source Code of dude.json

{
  "name": "Dude",
  "age": 47
}

By default, the object is strict, so you can only access fields which are present.

⬇️ Result | Source ➡️

  1. loose.String("Fake") (typed):
  2. loose.String("Fake"):
  3. loose.String("Fake", fallback: "I don't know!"): I don't know!
  4. loose.Int("Fake"): 0
  5. loose.Int("Fake", fallback: -1): -1
@inherits Custom.Hybrid.RazorTyped

@{
  // Variables for the tests
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";
  // Simple JSON strings which could come from a file or web service
  var jDude = System.IO.File.ReadAllText(jsonRoot + "/dude.json");
  var loose = Kit.Json.ToTyped(jDude, propsRequired: false);
}
<ol>
  <li>
    <code>loose.String("Fake")</code> (typed):
    @loose.String("Fake")
  </li>
  <li>
    <code>loose.String("Fake")</code>:
    @loose.String("Fake")
  </li>
  <li>
    <code>loose.String("Fake", fallback: "I don't know!")</code>:
    @loose.String("Fake", fallback: "I don't know!")
  </li>
  <li>
    <code>loose.Int("Fake")</code>:
    @loose.Int("Fake", required: false)
  </li>
  <li>
    <code>loose.Int("Fake", fallback: -1)</code>:
    @loose.Int("Fake", fallback: -1)
  </li>
</ol>

Source Code of dude.json

{
  "name": "Dude",
  "age": 47
}

JSON data can be difficult to work with, because you would need to use System.Text.Json which has a sophisticated API. Instead, we can convert it to ITyped and then use a very simple API.

⬇️ Result | Source ➡️

Marc

  1. marc.ToString(): { "name": "Marc", "age": 33, "car": null, "friends": [ { "name": "John", "age": 33 }, { "name": "Jane", "age": 33 } ] }
  2. name .String("Name") Marc
  3. age: 33
  4. birth year: 1991
  5. car make (will be null):
  6. Friends using AsTypedList(...)
    • John
    • Jane

Frank

  1. #2 frank.ToString(): { "name": "Frank", "age": 44, "car": { "make": "Ford", "model": "Focus" } }
  2. #2 name: Frank
  3. #2 car make: Ford
  4. #2 car ToString(): { "make": "Ford", "model": "Focus" }
@inherits Custom.Hybrid.RazorTyped

@{
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";

  // Simple JSON strings which could come from a file or web service
  var jMarc = System.IO.File.ReadAllText(jsonRoot + "/dude-marc.json");
  var jFrank = System.IO.File.ReadAllText(jsonRoot + "/dude-frank.json");

  // Convert the JSON to ITyped
  var marc = Kit.Json.ToTyped(jMarc);
  var frank = Kit.Json.ToTyped(jFrank);
  var year = DateTime.Now.Year;
}
<h4>Marc</h4>
<ol>
  <li>marc.ToString():
    <code>@marc.ToString()</code>
  </li>
  <li>name <code>.String("Name")</code>
    <code>@marc.String("Name")</code>
  </li>
  <li>age: <code>@marc.Get("Age")</code></li>
  <li>birth year:
    <code>@(year - marc.Int("Age"))</code>
  </li>
  <li>car make (will be null):
    <code>@marc.String("Car.Make", required: false)</code>
  </li>
  <li>
    Friends using <code>AsTypedList(...)</code>
    <ul>
      @foreach (var friend in AsTypedList(marc.Get("friends"))) {
        <li>@friend.String("Name")</li>
      }
    </ul>      
  </li>
</ol>
<h4>Frank</h4>
<ol>
  <li>#2 frank.ToString():
    <code>@frank.ToString()</code>
  </li>
  <li>#2 name:
    <code>@frank.String("Name")</code>
  </li>
  <li>#2 car make:
    <code>@frank.String("Car.Make")</code>
  </li>
  <li>#2 car ToString():
    <code>@frank.Get("Car").ToString()</code>
  </li>
</ol>

Source Code of dude-marc.json

{
  "name": "Marc",
  "age": 33,
  "car": null,
  "friends": [
    {
      "name": "John",
      "age": 33
    },
    {
      "name": "Jane",
      "age": 33
    }
  ]
}

Source Code of dude-frank.json

{
  "name": "Frank",
  "age": 44,
  "car": {
    "make": "Ford",
    "model": "Focus"
  }
}

⬇️ Result | Source ➡️

  • 1
  • 1
  • 2
  • 3
  • 5
  • 8
  • 13
  • 21
  • 34
  • 55
@inherits Custom.Hybrid.RazorTyped
@{
  // Simple JSON string containing fibonacci numbers
  var jFibonacci = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]";

  // Convert the JSON to int[]
  var fibonacci = Kit.Json.To<int[]>(jFibonacci);
}
<ul>
  @foreach (var f in fibonacci) {
    <li><code>@f</code></li>
  }
</ul>

⬇️ Result | Source ➡️

  • Buchs: 9470
    Partner Cities: Oklohoma, New York
  • Grabs: 0
  • Sevelen: 0
@inherits Custom.Hybrid.RazorTyped

@functions {
  // Custom class to deserialize JSON into
  // must be inside an @functions section
  public class City {
    public string Name { get; set; }
    public int ZipCode { get; set; }
    public List<City> PartnerCities { get;set; }
  }
}
@{
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";
  var jCities = System.IO.File.ReadAllText(jsonRoot + "/cities.json");

  // Convert the JSON to City
  var cities = Kit.Json.To<City[]>(jCities);
}
<ul>
  @foreach (var city in cities) {
    <li>
      <code>@city.Name</code>: @city.ZipCode
      @if (city.PartnerCities != null) {
        <br>
        <span>Partner Cities:
          @string.Join(", ", city.PartnerCities.Select(c => c.Name))
        </span>
      }
    </li>
  }
</ul>

Source Code of cities.json

[
  {
    "name": "Buchs",
    "zipCode": 9470,
    "partnerCities": [
      {
        "name": "Oklohoma"
      },
      {
        "name": "New York"
      }
    ]
  },
  {
    "name": "Grabs"
  },
  {
    "name": "Sevelen"
  }
]

If you have an array in your Json, you need to use ToTypedList.

⬇️ Result | Source ➡️

  1. Name: Buchs
  2. Name: Grabs
  3. Name: Sevelen
@inherits Custom.Hybrid.RazorTyped

@{
  // Variables for the tests
  string jsonRoot = App.Folder.PhysicalPath + "/json/quickref/json";
}
@{
  // Simple JSON strings which could come from a file or web service
  var jCities = System.IO.File.ReadAllText(jsonRoot + "/cities.json");

  // Convert the JSON to ITyped
  var cities = Kit.Json.ToTypedList(jCities);
}
<ol>
  @foreach (var city in cities) {
    <li>Name: <code>@city.String("Name")</code></li>
  }
</ol>

Source Code of cities.json

[
  {
    "name": "Buchs",
    "zipCode": 9470,
    "partnerCities": [
      {
        "name": "Oklohoma"
      },
      {
        "name": "New York"
      }
    ]
  },
  {
    "name": "Grabs"
  },
  {
    "name": "Sevelen"
  }
]

 

 

#1 Quick Reference for all APIs