This is a quick CheatSheet to provide you with a reference to most APIs you'll use.
@...
Common basics for working with Razor.
@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>
Create variables and show using @variableName
@variableName
Show old/not old based on Terry's age using @if and @else: this guy is old
@if
@else
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>
if/else and ternary operator (condition ? true : false)
(condition ? true : false)
Demostrates C# 6 and 7 in Razor. C# 7.3 must be installed this to work - see docs.
Demonstrates C# 6 features in Razor views.
@inherits Custom.Hybrid.Razor14 <h2>C# 6 Features</h2> <p> Demonstrates C# 6 features in Razor views. </p> <h3>Call various properties / functions C# 6</h3> <ol> <li>Expression bodied Function: @GetAge()</li> <li>Expression bodied Property: @Age</li> <li>Auto property initializer: @AgeProperty</li> <li>Read-Only property initializer: @AgeReadOnlyProp</li> </ol> @functions { // expression bodied function - C# 6.0 public string GetAge() => "62"; // expression bodied properties - C# 6.0 public int Age => 62; // Auto property initializers - C# 6.0 public int AgeProperty { get; set; } = 62; public int AgeReadOnlyProp { get; } = 62; // Index initializers - C# 6.0 public Dictionary<string, string> Dict => new Dictionary<string, string>() { ["key1"] = "value1", ["key2"] = "value2", ["key2"] = "value3" // this will overwrite the previous value }; } @{ const int old = 60; const string isOld = "this guy is old"; const string notOld = "this guy is not old"; var age = 62; string testNull = null; var length = testNull?.Length; var array = new[] { 1, 2, 3 }; var first = array?[0]; var message = $"this is a {nameof(age)}"; } <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>
@foreach (var thing in list)
@for (counter; condition; increment)
@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>
for
foreach
Loops using for() and foreach()
for()
foreach()
@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>
Learn the difference of showing variables with @variable and @Html.Raw(variable), and re-use very simple snippets
@variable
@Html.Raw(variable)
You can just use any Emoji in your source. Or you pick them with ternary operations.
@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>
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.
Normally you would use the following in an href attribute.
href
https://2sxc.org/dnn-tutorials/en/razor
ui=home&variant=strong&tut=quickref&id=48833
https://2sxc.org/dnn-tutorials/en/razor/ui/home/variant/strong/tut/quickref/id/48833
https://2sxc.org/dnn-tutorials/en/razor/ui/home/variant/strong/tut/quickref/id/48833/another/more/27/more/42
https://2sxc.org/dnn-tutorials/en/razor/ui/home/variant/strong/tut/quickref/id/48833/quick-ref-typed/test
This only works if you don't have a page 12, otherwise you'll see an error in the url.
https://2sxc.org/dnn-tutorials/en/razor#error-unknown-pageid-12
https://2sxc.org/dnn-tutorials/en/razor/ui/home/variant/strong/tut/quickref/id/48833#error-unknown-pageid-12
@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>
Don't-Repeat-Yourself (DRY) by reusing code in Razor and helper C# files.
@functions
@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>
Normal C# functions are the basic block for reusing code.
Use a shared razor file to hold multiple functions / helpers, and call them one-by-one as needed.
Explains the basics of re-using code across templates and code 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>
@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;">
Reuse Razor files by calling them using @Html.Partial and passing parameters in DynamicModel (or MyModel).
@Html.Partial
@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>
@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>
.cs
Reuse Partial Razor and functions from Helper Code files.
@inherits Custom.Hybrid.RazorTyped @{ // helper library to say hello & create QR codes var qrLib = GetCode("../../shared/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>
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; } }
Razor doesn't know about types inside code files, so you need some tricks to work with objects which are passed around.
The only way to get values from an anonymous object created elsewhere is using AsTyped(... as object). Everything else will 🚫.
AsTyped(... as object)
ITyped
If the returned object is based on ITyped, things are a bit easier. But you must still ensure that Razor knows the type.
If the returned object is based on a class, things are a bit easier.
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.
dynamic
@inherits Custom.Hybrid.RazorTyped @{ // helper library which will return object var lib = GetCode("./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>
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; } } }
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.
Id, Guid and Title are built-in properties
Since person is fully typed, you can just call it's properties.
person
Use Get(…) if you don't care about the var type or Get<T>(…) or typed methods such as .String(…).
Get(…)
Get<T>(…)
.String(…)
Use fallback: … to handle empty values or conversion problems.
fallback: …
Use .Attribute(…) to safely encode properties. Mouse over this to see the effect.
.Attribute(…)
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data @{ var person = As<Persons>(MyItem); } <h4>Douglas Adams on <code>MyItem</code></h4> <p> Id, Guid and Title are built-in properties </p> <ol> <li>Id: @person.Id</li> <li>Guid: @person.Guid</li> <li>Title: @person.Title</li> </ol> <p>Since <code>person</code> is fully typed, you can just call it's properties.</p> <ol> <li>Name: @person.FirstName</li> <li>Birthday: @person.Birthday</li> <li>Is Alive: @person.IsAlive</li> <li>Fav Num. Int: @person.FavoriteNumber</li> </ol> <p>Use <code>Get(…)</code> if you don't care about the var type or <code>Get<T>(…)</code> or typed methods such as <code>.String(…)</code>.</p> <ol> <li>Name: @person.Get("FirstName")</li> <li>Birthday: @person.Get("Birthday")</li> <li>Birthday Get<string> @(person.Get<string>("Birthday"))</li> <li>Birthday Get<DateTime> @(person.Get<DateTime>("Birthday"))</li> <li>Name (strings): @person.String("FirstName")</li> <li>Birthday: @person.DateTime("Birthday").ToString("d")</li> <li>Is Alive: @person.Bool("IsAlive")</li> <li>Fav Num. Int: @person.Int("FavoriteNumber")</li> <li>Fav Num. Float: @person.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): @person.String("FirstName", fallback: "unknown") <li>Name (int): @person.Int("FirstName", fallback: 12345) </ol> <p title='@person.Attribute("FirstName")'> Use <code>.Attribute(…)</code> to safely encode properties. Mouse over this to see the effect. </p>
namespace AppCode.Data { public partial class Persons { /// <summary> /// Custom property Presentation - as in these tutorials /// the Person always uses a QuickRefContentPresentation content-type for the presentation /// </summary> public new QuickRefContentPresentation Presentation => _presentation ??= As<QuickRefContentPresentation>(base.Presentation); private QuickRefContentPresentation _presentation; } }
// DO NOT MODIFY THIS FILE - IT IS AUTO-GENERATED // See also: https://go.2sxc.org/copilot-data // To extend it, create a "Persons.cs" with this contents: /* namespace AppCode.Data { public partial class Persons { // Add your own properties and methods here } } */ // Generator: CSharpDataModelsGenerator v17.08.00 // App/Edition: Tutorial-Razor/ // User: 2sic Web-Developer // When: 2024-05-21 19:46:38Z using System; using System.Collections.Generic; using System.Text.Json.Serialization; using ToSic.Sxc.Adam; using ToSic.Sxc.Data; namespace AppCode.Data { // This is a generated class for Persons // To extend/modify it, see instructions above. /// <summary> /// Persons data. <br/> /// Generated 2024-05-21 19:46:38Z. Re-generate whenever you change the ContentType. <br/> /// <br/> /// Default properties such as `.Title` or `.Id` are provided in the base class. <br/> /// Most properties have a simple access, such as `.Awards`. <br/> /// For other properties or uses, use methods such as /// .IsNotEmpty("FieldName"), .String("FieldName"), .Children(...), .Picture(...), .Html(...). /// </summary> public partial class Persons: AutoGenerated.ZAutoGenPersons { } } namespace AppCode.Data.AutoGenerated { /// <summary> /// Auto-Generated base class for Default.Persons in separate namespace and special name to avoid accidental use. /// </summary> public abstract class ZAutoGenPersons: Custom.Data.CustomItem { /// <summary> /// Awards as list of PersonAwards. /// </summary> /// <remarks> /// Generated to return child-list child because field settings had Multi-Value=true. The type PersonAwards was specified in the field settings. /// </remarks> /// <returns> /// An IEnumerable of specified type, but can be empty. /// </returns> public IEnumerable<PersonAwards> Awards => _awards ??= _item.Children<PersonAwards>("Awards"); private IEnumerable<PersonAwards> _awards; /// <summary> /// Biography as string. <br/> /// For advanced manipulation like scrubHtml, use .String("Biography", scrubHtml: true) etc. /// </summary> public string Biography => _item.String("Biography", fallback: ""); /// <summary> /// Birthday as DateTime. /// </summary> public DateTime Birthday => _item.DateTime("Birthday"); /// <summary> /// FavoriteNumber as int. <br/> /// To get other types use methods such as .Decimal("FavoriteNumber") /// </summary> public int FavoriteNumber => _item.Int("FavoriteNumber"); /// <summary> /// FirstName as string. <br/> /// For advanced manipulation like scrubHtml, use .String("FirstName", scrubHtml: true) etc. /// </summary> public string FirstName => _item.String("FirstName", fallback: ""); /// <summary> /// Haters as single item of ITypedItem. /// </summary> /// <remarks> /// Generated to only return 1 child because field settings had Multi-Value=false. /// </remarks> /// <returns> /// A single item OR null if nothing found, so you can use ?? to provide alternate objects. /// </returns> public ITypedItem Haters => _haters ??= _item.Child("Haters"); private ITypedItem _haters; /// <summary> /// IsAlive as bool. <br/> /// To get nullable use .Get("IsAlive") as bool?; /// </summary> public bool IsAlive => _item.Bool("IsAlive"); /// <summary> /// LastName as string. <br/> /// For advanced manipulation like scrubHtml, use .String("LastName", scrubHtml: true) etc. /// </summary> public string LastName => _item.String("LastName", fallback: ""); /// <summary> /// Mugshot as link (url). <br/> /// To get the underlying value like 'file:72' use String("Mugshot") /// </summary> public string Mugshot => _item.Url("Mugshot"); /// <summary> /// Get the file object for Mugshot - or null if it's empty or not referencing a file. /// </summary> [JsonIgnore] public IFile MugshotFile => _item.File("Mugshot"); /// <summary> /// Get the folder object for Mugshot. /// </summary> [JsonIgnore] public IFolder MugshotFolder => _item.Folder("Mugshot"); /// <summary> /// Sex as string. <br/> /// For advanced manipulation like scrubHtml, use .String("Sex", scrubHtml: true) etc. /// </summary> public string Sex => _item.String("Sex", fallback: ""); } }
This is how this view would be configured for this sample.
Every thing is an Entity. Here some basic examples how to show values like Name, Birthday etc. of such an Entity.
Show content which was entered for this module
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…
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 comedy, The 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>
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.
Note that the following should be green (probably green)
Content number 1
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data <h4>A Person on <code>MyItem</code></h4> @{ var itemPres = As<QuickRefContentPresentation>(MyItem.Presentation); } <p> Note that the following should be @itemPres.Color (probably green) </p> <p style='color: @itemPres.Attribute("Color")'> @MyItem.Title </p>
// DO NOT MODIFY THIS FILE - IT IS AUTO-GENERATED // See also: https://go.2sxc.org/copilot-data // To extend it, create a "QuickRefContentPresentation.cs" with this contents: /* namespace AppCode.Data { public partial class QuickRefContentPresentation { // Add your own properties and methods here } } */ // Generator: CSharpDataModelsGenerator v17.08.00 // App/Edition: Tutorial-Razor/ // User: 2sic Web-Developer // When: 2024-05-21 19:46:38Z namespace AppCode.Data { // This is a generated class for QuickRefContentPresentation // To extend/modify it, see instructions above. /// <summary> /// QuickRefContentPresentation data. <br/> /// Generated 2024-05-21 19:46:38Z. Re-generate whenever you change the ContentType. <br/> /// <br/> /// Default properties such as `.Title` or `.Id` are provided in the base class. <br/> /// Most properties have a simple access, such as `.Color`. <br/> /// For other properties or uses, use methods such as /// .IsNotEmpty("FieldName"), .String("FieldName"), .Children(...), .Picture(...), .Html(...). /// </summary> public partial class QuickRefContentPresentation: AutoGenerated.ZAutoGenQuickRefContentPresentation { } } namespace AppCode.Data.AutoGenerated { /// <summary> /// Auto-Generated base class for Default.QuickRefContentPresentation in separate namespace and special name to avoid accidental use. /// </summary> public abstract class ZAutoGenQuickRefContentPresentation: Custom.Data.CustomItem { /// <summary> /// Color as string. <br/> /// For advanced manipulation like scrubHtml, use .String("Color", scrubHtml: true) etc. /// </summary> public string Color => _item.String("Color", fallback: ""); /// <summary> /// HeadingType as string. <br/> /// For advanced manipulation like scrubHtml, use .String("HeadingType", scrubHtml: true) etc. /// </summary> public string HeadingType => _item.String("HeadingType", fallback: ""); /// <summary> /// Highlight as bool. <br/> /// To get nullable use .Get("Highlight") as bool?; /// </summary> public bool Highlight => _item.Bool("Highlight"); } }
Child items such as Awards are accessed using Child("Awards"). There are many ways to work with them.
Awards
Child("Awards")
.Child("Awards")
Use ContainsKey(…), IsEmpty(…), IsNotEmpty(…)
.Children("Awards")
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data <h4>Use <code>.Child("Awards")</code> to get one</h4> @{ var person = As<Persons>(MyItem); var awards = person.Awards; // of type List<PersonAwards> var award = awards.FirstOrDefault(); } <ol> <li>Award ID: @award.Id</li> <li>Award Name: @award.Name</li> <li>Award Name (Path): @person.String("Awards.Name")</li> </ol> <p>Use ContainsKey(…), IsEmpty(…), IsNotEmpty(…)</p> <ol> <li>ContainsKey("Awards"): @person.ContainsKey("Awards")</li> <li>ContainsKey("Awards2"): @person.ContainsKey("Awards2")</li> <li>IsEmpty("Awards"): @person.IsEmpty("Awards")</li> <li>IsNotEmpty("Awards"): @person.IsNotEmpty("Awards")</li> <li>IsEmpty("Awards.Name"): @person.IsEmpty("Awards.Name")</li> <li>IsEmpty("Awards2.Name"): @person.IsEmpty("Awards2.Name")</li> <li>IsEmpty("Awards.NameX"): @person.IsEmpty("Awards.NameX")</li> </ol> <h4>Use <code>.Children("Awards")</code> to get all</h4> <span>Award Count: @person.Awards.Count()</span> <ol> @foreach (var personAward in person.Awards) { <li>Award: @personAward.Name</li> } </ol>
// DO NOT MODIFY THIS FILE - IT IS AUTO-GENERATED // See also: https://go.2sxc.org/copilot-data // To extend it, create a "PersonAwards.cs" with this contents: /* namespace AppCode.Data { public partial class PersonAwards { // Add your own properties and methods here } } */ // Generator: CSharpDataModelsGenerator v17.08.00 // App/Edition: Tutorial-Razor/ // User: 2sic Web-Developer // When: 2024-05-21 19:46:38Z namespace AppCode.Data { // This is a generated class for PersonAwards // To extend/modify it, see instructions above. /// <summary> /// PersonAwards data. <br/> /// Generated 2024-05-21 19:46:38Z. Re-generate whenever you change the ContentType. <br/> /// <br/> /// Default properties such as `.Title` or `.Id` are provided in the base class. <br/> /// Most properties have a simple access, such as `.Name`. <br/> /// For other properties or uses, use methods such as /// .IsNotEmpty("FieldName"), .String("FieldName"), .Children(...), .Picture(...), .Html(...). /// </summary> public partial class PersonAwards: AutoGenerated.ZAutoGenPersonAwards { } } namespace AppCode.Data.AutoGenerated { /// <summary> /// Auto-Generated base class for Default.PersonAwards in separate namespace and special name to avoid accidental use. /// </summary> public abstract class ZAutoGenPersonAwards: Custom.Data.CustomItem { /// <summary> /// Name as string. <br/> /// For advanced manipulation like scrubHtml, use .String("Name", scrubHtml: true) etc. /// </summary> public string Name => _item.String("Name", fallback: ""); } }
Properties can contain urls such as /abc.jpg or file references like file:72.
/abc.jpg
file:72
Use .Url(…) to resolve file references such as file:72
.Url(…)
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data <h4>Douglas Adams, the current item (MyItem)</h4> @{ var person = As<Persons>(MyItem); } <p>Use <code>.Url(…)</code> to resolve file references such as <code>file:72</code></p> <ol> <li>Mugshot URL: @person.Mugshot</li> <li>Mugshot field Value: @person.String("Mugshot")</li> <li> Mugshot Picture <br> @person.Picture("Mugshot", settings: "Square", width: 100, imgClass: "rounded-circle") </li> </ol>
Every file-field is actually a folder...
...which could hold many files. If you want to show them, you need Kit.Image...
Kit.Image...
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data <h4>Douglas Adams, the current item (MyItem)</h4> @{ var person = As<Persons>(MyItem); // The ...File property is autogenerated for every file/link-field var file = person.MugshotFile; 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> @{ // The ...Folder property is autogenerated for every file/link-field var folder = person.MugshotFolder; } <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>
@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>
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.
@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>
MyItems
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data <h4>Loop persons which were added to this view</h4> <ul> @foreach (var person in AsList<Persons>(MyItems)) { <li> @person.Picture("Mugshot", settings: "Square", width: 50, imgClass: "rounded-circle") @person.FirstName @person.LastName </li> } </ul>
We are one!
Two be or !2B
Three's the charm
@inherits Custom.Hybrid.RazorTyped @using AppCode.Data @{ // Customize the items so that they are typed, with presentation of another known type var persons = AsList<Persons>(MyItems); } <ul> @foreach (var item in persons) { // This is typed as QuickRefContentPresentation var pres = item.Presentation; // check if empty, and using nameof to be sure we spelled it right var headingTagExists = pres.IsNotEmpty(nameof(pres.HeadingType)); var title = item.Title + (pres.Highlight ? " 🌟" : ""); <li> @* Create a heading tag the size specified in Presentation *@ @if (headingTagExists) { @Kit.HtmlTags.Custom(pres.HeadingType).Wrap(title) } else { @title } <br> <em>FYI: Heading @(pres.IsEmpty("HeadingType") ? "none" : pres.HeadingType)</em> <div style='color: @pres.Attribute("Color")'> @item.Html("Contents") </div> </li> } </ul>
@inherits Custom.Hybrid.RazorTyped <h3>@MyHeader.Title</h3> <ul> @foreach (var item in MyItems) { <li> @item.Title </li> } </ul>
MyData
App.GetQuery(...)
AsItems(...)
Every view/template receives prepared data, either entered by the user on this page, or provided through a query.
@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>
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(…).
Default
Selected
.Equals(…)
@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>
Authors
@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>
@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>
@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 *@
Settings can be configured at many levels, the most-local (eg. the App) would override the most global (eg. Presets).
#E91E63F2
file:6120
/Portals/tutorials/adam/Tutorial-Razor/FP6nrGBJZUKPRnldQOMQEQ/CsvFile/products.csv
1400
Settings
{"latitude":47.1747,"longitude":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>
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 + "/assets/icons/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"); }
This sample modifies the HTML head, so it's not visible here.
To see the effect, look at the source in the browser
Get/Set Page Title, Keywords, Description and set meta-tags and more.
Add a <base> tag to the header. This is important for SPA JS applications.
Add various combinations of icons to the page header
Add all kinds of meta-tags to the header.
Set OpenGraph Headers etc.
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.
Kit.Page.SetTitle(...)
title
meta title
meta description
meta keywords
@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>
Add Open-Graph data headers for Facebook, Twitter and other sharing-systems
Add JSON-LD (Linked Data) headers for Google
Get Info about Platform, Culture/Languages etc.
Dnn
9.11.0.46
en-us
24
https://2sxc.org/dnn-tutorials/en
2sxc.org/dnn-tutorials/en
1193
4480
0
00000000-0000-0000-0000-000000000000
-1
False
49932
Default Tutorial Page (new v16)
9RSSoP4y
@inherits Custom.Hybrid.RazorTyped <h4>Platform</h4> <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> </ol> <h4>Culture</h4> <ol> <li>Culture Current Code: '<code>@MyContext.Culture.CurrentCode</code>'</li> <li>Culture Default Code: '<code>@MyContext.Culture.DefaultCode</code>'</li> </ol> <h4>Site</h4> <ol> <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> </ol> <h4>Page</h4> <ol> <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> </ol> <h4>Module & Block</h4> <ol> <li>Module Id: '<code>@MyContext.Module.Id</code>'</li> <li>Block Id / Guid: '<code>@MyContext.Block.Id</code>' '<code>@MyContext.Block.Guid</code>' </li> <li>Module Block Id: '<code>@MyContext.Module.Block.Id</code>' '<code>@MyContext.Module.Block.Guid</code>' </li> </ol> <h4>User</h4> <ol> <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> </ol> <h4>View</h4> <ol> <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> </ol> <h4>Unique Key</h4> <ol> <li>Unique Key '<code>@UniqueKey</code>'</li> </ol>
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.
<script src="..."<
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/tutorials/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>
// 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.';
// 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.
turnOn
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/tutorials/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)
// 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; }
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.
UniqueKey
This separate Razor will create a div for the resulting message, based on the same UniqueKey which is 9RSSoP4y
(script didn't run yet - this elements id is demo-uniquekey-msg-9RSSoP4y)
demo-uniquekey-msg-9RSSoP4y
@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/tutorials/js-quickref/js/unique-key.js"></script>
@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>
// 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(...).
@Kit.Key.UniqueKeyWith(...)
12345
9RSSoP4y-n12345
"hello"
9RSSoP4y-s-327419862
"bad chars in id ! % / 👍🏽"
9RSSoP4y-s-1348749150
"this is a long text and should be shortened"
9RSSoP4y-s527251735
9RSSoP4y-n12345-s-327419862
@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.
Hitchhikers Guide to the Galaxy
9RSSoP4y-eid7md5eRsH
Good Omens
9RSSoP4y-eidcAcM-LSz
Phishing for Phools
9RSSoP4y-eidlkSe34Oo
The Last Continent
9RSSoP4y-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 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.
System.Text.Json
.Get("name")
.Get("NAME")
@inherits Custom.Hybrid.RazorTyped @{ // Variables for the tests string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>
{ "name": "Dude", "age": 47 }
This example shows more advanced scenarios. It uses the previously created dude object.
dude
.String("Name")
.Int("Age")
147
10047
@inherits Custom.Hybrid.RazorTyped @{ // Variables for the tests string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>
By default, the object is strict, so you can only access fields which are present.
strict.String("Fake")
strict.String("Fake", required: false)
strict.String("Fake", required: false, fallback: "undefined")
strict.Int("Fake", required: false)
strict.Int("Fake", required: false, fallback: -1)
@inherits Custom.Hybrid.RazorTyped @{ // Variables for the tests string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>
loose.String("Fake")
loose.String("Fake", fallback: "I don't know!")
loose.Int("Fake")
loose.Int("Fake", fallback: -1)
@inherits Custom.Hybrid.RazorTyped @{ // Variables for the tests string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>
{ "name": "Marc", "age": 33, "car": null, "friends": [ { "name": "John", "age": 33 }, { "name": "Jane", "age": 33 } ] }
Marc
33
1991
AsTypedList(...)
{ "name": "Frank", "age": 44, "car": { "make": "Ford", "model": "Focus" } }
Frank
Ford
{ "make": "Ford", "model": "Focus" }
@inherits Custom.Hybrid.RazorTyped @{ string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>
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>
Buchs
Grabs
Sevelen
@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 + "/tutorials/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>
[ { "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.
ToTypedList
@inherits Custom.Hybrid.RazorTyped @{ // Variables for the tests string jsonRoot = App.Folder.PhysicalPath + "/tutorials/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>