33
Razor Revealed Jeavon Leopold | Twitter: @crumpled_Jeavon We are Crumpled Dog

Umbraco OktoberFest 2014

Embed Size (px)

Citation preview

Page 1: Umbraco OktoberFest 2014

Razor Revealed

Jeavon Leopold | Twitter: @crumpled_Jeavon

We are Crumpled Dog

Page 2: Umbraco OktoberFest 2014

You are on page 222/01/2015

I have been working with Umbraco since version 3.0 (circa 2007)

• Technical Director at Crumpled Dog – Umbraco Gold Partner in London’s TechCity/Silicon Roundabout

• Umbraco MVP – Honoured to be voted by the global community as one of the five 2104 MVPs

• Umbraco Community Member – I am known for answering

many questions in the Razor forum and have 7,500 karma

• Umbraco Core Contributor– I have made many bug fixes and

even added a few features here and there over the years.

Most recently I worked with Per Ploug Hansen and James

South on the v7.1 Core Image Cropper API

• Umbraco Package Hacker – I have created a few packages

of my own and collaborated on many more

Who am I?

Page 3: Umbraco OktoberFest 2014

What is Razor?

You are on page 322/01/2015

It’s a ASP.NET MVC View Engine

• Created to be easy to pick up by designers, front end developers and general tinkerers

• C sharp (C#) HTML (does also support VB HTML, but we don’t talk about that)

• You add code to your script by using the @ character

• You use braces { } for code blocks and semicolons ; to break statements and all other standard C# syntax@{ var myCounter = 1; }

• When you display content using the @ character ASP.NET HTML encodes the output, so character such as <, > and & are rendered without begin interpreted as HTML

• You use standard C# conditional and loop logic, such as foreach, while and if

• You can use the @: or the <text> element to combine code, text and markupif (DateTime.Now.DayOfWeek == DayOfWeek.Thursday){

@:Today is Thursday the @DateTime.Now.Day}

• Comes with some helpful helpers for converting strings to other typesvar mycount = myString.AsInt();

• Razor Views should be fairly simple, if they have lots of logic in them, then you probably need to think differently

Page 4: Umbraco OktoberFest 2014

Razor and Umbraco

You are on page 422/01/2015

Confusion!

DynamicNode Macro Scripts (Umbraco v4.7.2+)

• Do not use the legacy Macro Scripts ever!

• DynamicNode scripts inherit@inherits umbraco.MacroEngines.DynamicNodeContext

• You access Umbraco content using Model

IPublishedContent in a View, Partial View or a Partial View Macro (Umbraco v4.10.0+)

• Generally IPublishedContent Views & Partial Views inherit@inherits Umbraco.Web.Mvc.UmbracoTemplatePage

• IPublishedContent Partial View Macros inherit@inherits Umbraco.Web.Macros.PartialViewMacroPage

• You access Umbraco content using CurrentPage (dynamic) or Model.Content (strongly typed)

Page 5: Umbraco OktoberFest 2014

Dynamic vs Strongly Typed

You are on page 522/01/2015

Conciseness

@[email protected]("myProperty")

IntelliSense, pre-compilation errors in Visual Studio or WebMatrix & any errors at all

Page 6: Umbraco OktoberFest 2014

Task 1a: Children Collections

You are on page 622/01/2015

Render a list of links linking to all nodes of document type alias “umbTextPage” that are children ofthe homepage on the homepage

Dynamic model has the magical pluraliser

@foreach (var page in CurrentPage.umbTextPages)

{

<div class="3u">

<!-- Feature -->

<section class="is-feature">

<a href="@page.Url" class="image image-full">

<img src="@page.Image" alt="" />

</a>

<h3><a href="@page.Url">@page.Name</a></h3>

@Umbraco.Truncate(page.BodyText, 100)

</section>

</div>

}

Files: /Views/umbHomePage.cshtml

Page 7: Umbraco OktoberFest 2014

Task 1a (cont): Children Collections

You are on page 722/01/2015

Typed model has the OfTypes method

@foreach (var page in Model.Content.Children.OfTypes("umbTextPage"))

{

<div class="3u">

<!-- Feature -->

<section class="is-feature">

<a href="@page.Url" class="image image-full">

<img src="@page.GetPropertyValue("Image")" alt="" />

</a>

<h3><a href="@page.Url">@page.Name</a></h3>

@Umbraco.Truncate(page.GetPropertyValue<string>("BodyText"), 100)

</section>

</div>

}

Files: /Views/umbHomePage.cshtml

Page 8: Umbraco OktoberFest 2014

What is that @Umbraco.?

You are on page 822/01/2015

Say hello to the UmbracoHelper, loads of helpful methods for your scripts

Content

.Content(int id);

.ContentAtRoot();

.ContentAtXPath(string xpath, params XPathVariable[] variables);

.ContentSingleAtXPath(string xpath, params XPathVariable[] variables);

.TypedContent(int id)

.TypedContentAtRoot()

.TypedContentAtXPath(string xpath)

.TypedContentSingleAtXPath(string xpath)

Content helpers

.Field(string field)

.NiceUrl(int nodeId)

.NiceUrlWithDomain(int id)

Working with Media

.Media(1234);

.MediaAtRoot();

.TypedMedia(int id)

.TypedMediaAtRoot();

Working with Members.Member(1234);.TypedMember(1234);

Member Helpers.MemberHasAccess(int nodeId, string path);.MemberIsLoggedOn().IsProtected(int pageId, string path)

Fetching misc data.GetDictionaryValue(string key);.GetPreValueAsString(int prevalueId).Search(string term, bool useWildCards, string searchProvider).TypedSearch(string term, bool useWildCards, string searchProvider)

Templating Helpers.Coalesce(params object[]);.Concatenate(params object[]);.CreateMd5Hash(string text);.If(bool test, string valueIfTrue, string valueIfFalse).Join(string seperator, parmas object[] args) .ReplaceLineBreaksForHtml(string text).StripHtml(string html).Truncate(string html, int length, bool addElipsis).RenderMacro(string alias, object parameters).RenderTemplate(int pageId, int? altTemplateId)

Page 9: Umbraco OktoberFest 2014

Task 1b: Children Collections

You are on page 922/01/2015

Render a list of links linking to all nodes of document type alias “umbTextPage” or “umbNewsOverview” that are children of the current page

Dynamic model has a Where method which you can pass a filter string

@foreach (var page in CurrentPage.Children.Where("DocumentTypeAlias == \"umbTextPage\" || DocumentTypeAlias == \"umbNewsOverview\""))

Typed model, we just add parameters to the OfTypes method

@foreach (var page in Model.Content.Children.OfTypes("umbTextPage",

"umbNewsOverview"))

Files: /Views/umbHomePage.cshtml

Page 10: Umbraco OktoberFest 2014

Task 1c: Children Collections

You are on page 1022/01/2015

What if there were more document types?

Dynamic model we can use a Where method with the ContainsAny string extension and pass in a list object

@{

var docTypesToInclude = new List<string>() { "umbTextPage",

"umbNewsOverview" };

<ul>

@foreach (var page in CurrentPage.

Children.

Where("DocumentTypeAlias.ContainsAny(@0)",

docTypesToInclude))

{

<li><a href="@page.Url">@page.Name</a></li>

}

</ul>

}

Typed model, I think we have it covered already?

Files: /Views/umbHomePage.cshtml

Page 11: Umbraco OktoberFest 2014

Task 1d: Children Collections

You are on page 1122/01/2015

What about other filtering conditions and ordering?

Dynamic model we can add brackets and add and/or (&&/||) operators and the OrderBy method

@foreach (var page in CurrentPage.Children.Where("(DocumentTypeAlias == \"umbTextPage\" || DocumentTypeAlias == \"umbNewsOverview\") && featuredPage").OrderBy("Name desc"))

Typed model, we add the Where and OrderBy methods and pass in a lambda

@foreach (var page in Model.Content.Children.OfTypes("umbTextPage",

"umbNewsOverview")

.Where(x => x.GetPropertyValue<bool>("featuredPage"))

.OrderByDescending(x => x.Name))

Files: /Views/umbHomePage.cshtml

Page 12: Umbraco OktoberFest 2014

Reuse?

You are on page 1222/01/2015

Partial Views

• Easy to create in the Umbraco UI

• Can be strongly typed so you can pass in the model context for rendering

• Can be passed parameters using a ViewDataDictionary object

Functions & Helpers

• In-script reuse

• Both can be passed parameters

Global Functions & Helpers

• Makes functions and helpers reusable across different scripts

C# Extension Methods

• Logic can be reused between Razor and other C# logic (e.g. WebApi or Surface Controllers)

Page 13: Umbraco OktoberFest 2014

Empty attributes?

You are on page 1322/01/2015

A very useful little feature of Razor is that if you attempt to render a null value into a HTML attribute it will not render the attribute at all!

var current = CurrentPage.Id == page.Id ? "current_page_item" : null;

<li><a class="@current" href="@page.Url">@page.Name</a></li>

umbracoNaviHide ConventionDynamics model@foreach (var page in CurrentPage.Children.Where("Visible").OrderBy("Name desc"))

Typed model@foreach (var page in Model.Content.Children.Where(x => x.IsVisible())

Page 14: Umbraco OktoberFest 2014

Tree Traversal

You are on page 1422/01/2015

IPublishedContent has XPath like axes for traversing content, returning collections or single nodes

Children()

Ancestors()

Ancestors(int level)

Ancestors(string nodeTypeAlias)

AncestorsOrSelf()

AncestorsOrSelf(int level)

AncestorsOrSelf(string nodeTypeAlias)

Descendants()

Descendants(int level)

Descendants(string nodeTypeAlias)

DescendantsOrSelf()

DescendantsOrSelf(int level)

DescendantsOrSelf(string nodeTypeAlias)

AncestorOrSelf()AncestorOrSelf(int level)AncestorOrSelf(string nodeTypeAlias)AncestorOrSelf(Func<IPublishedContent, bool> func)Up()Up(int number)Up(string nodeTypeAlias)Down()Down(int number)Down(string nodeTypeAlias)Next()Next(int number)Next(string nodeTypeAlias)Previous()Previous(int number)Previous(string nodeTypeAlias)Sibling(int number)Sibling(string nodeTypeAlias)

Page 15: Umbraco OktoberFest 2014

Task 2a: Navigation

You are on page 1522/01/2015

Create a reusable (needs to work at all node levels) main navigation using a Partial View

Dynamics model

@inherits UmbracoTemplatePage

<nav id="nav" class="skel-panels-fixed">

<ul>

@foreach (var page in CurrentPage

.AncestorOrSelf(1)

.Children

.Where("Visible"))

{

var current = CurrentPage.Id == page.Id ? "current_page_item" : null;

<li><a class="@current" href="@page.Url">@page.Name</a></li>

}

</ul>

</nav>

Files: /Views/Partials/umbTopNavigation.cshtml

Page 16: Umbraco OktoberFest 2014

Task 2a (cont): Navigation

You are on page 1622/01/2015

Typed model

@inherits UmbracoTemplatePage

<nav id="nav" class="skel-panels-fixed">

<ul>

@foreach (var page in Model

.Content

.AncestorOrSelf(1)

.Children

.Where(x => x.IsVisible()))

{

var current = Model.Content.Id == page.Id

? "current_page_item"

: null;

<li><a class="@current" href="@page.Url">@page.Name</a></li>

}

</ul>

</nav>

Files: /Views/Partials/umbTopNavigation.cshtml

Page 17: Umbraco OktoberFest 2014

Task 2b: Navigation – homepage?

You are on page 1722/01/2015

Dynamic model – Use a Razor helper

@inherits UmbracoTemplatePage

<nav id="nav" class="skel-panels-fixed">

<ul>

@{

var home = CurrentPage.AncestorOrSelf(1);

@RenderNavForNode(home)

foreach (var page in home.Children.Where("Visible"))

{

@RenderNavForNode(page);

}

}

</ul>

</nav>

@helper RenderNavForNode(dynamic page)

{

var current = CurrentPage.Id == page.Id ? "current_page_item" : null;

<li><a class="@current" href="@page.Url">@page.Name</a></li>

}

Files: /Views/Partials/umbTopNavigation.cshtml

Page 18: Umbraco OktoberFest 2014

Task 2b (cont): Navigation – homepage?

You are on page 1822/01/2015

Typed model

@inherits UmbracoTemplatePage

<nav id="nav" class="skel-panels-fixed">

<ul>

@{

var home = Model.Content.AncestorOrSelf(1);

@RenderNavForNode(home)

foreach (var page in home.Children.Where("Visible"))

{

@RenderNavForNode(page);

}

}

</ul>

</nav>

@helper RenderNavForNode(IPublishedContent page)

{

var current = Model.Content.Id == page.Id ? "current_page_item" : null;

<li><a class="@current" href="@page.Url">@page.Name</a></li>

}

Files: /Views/Partials/umbTopNavigation.cshtml

Page 19: Umbraco OktoberFest 2014

Task 3a: strongly typed partial view

You are on page 1922/01/2015

Convert task 1 to a strongly typed partial view

Typed model

@foreach (var page in Model.Content.Children

.OfTypes("umbTextPage","umbNewsOverview")

.Where(x => x.GetPropertyValue<bool>("featuredPage"))

.OrderByDescending(x => x.Name)){

@Html.Partial("umbFeatures", page)

}

Files: /Views/umbHomePage.cshtml

Page 20: Umbraco OktoberFest 2014

Task 3b: dynamic strongly typed partial view

You are on page 2022/01/2015

What, are you sure?

Dynamic model

@foreach (var page in CurrentPage.Children

.Where("(DocumentTypeAlias == \"umbTextPage\" || DocumentTypeAlias == \"umbNewsOverview\") && featuredPage")

.OrderBy("Name desc"))

{

@Html.Partial("umbFeaturesDynamic", (IPublishedContent)page)

}

Files: /Views/umbHomePage.cshtml

Page 21: Umbraco OktoberFest 2014

Task 3b (cont): strongly typed partial view

You are on page 2122/01/2015

AsDynamic()

When using the dynamic model sometimes you find you need to use the typed model for something such as a complex filter. You can switch back to dynamic for rendering by using the AsDynamic() method.

Dynamic strongly typed partial view (ish)

@inherits UmbracoViewPage<IPublishedContent>

@{

var currentPage = Model.AsDynamic();

}

<div class="3u">

<!-- Feature -->

<section class="is-feature">

<a href="@currentPage.Url" class="image image-full"><imgsrc="@currentPage.Image" alt="" /></a>

<h3><a href="@currentPage.Url">@currentPage.Name</a></h3>

@Umbraco.Truncate(currentPage.BodyText, 100)

</section>

</div>

Files: /Views/Partials/umbFeatureDynamic.cshtml

Page 22: Umbraco OktoberFest 2014

Task 4a: passing parameters to a Partial View

You are on page 2222/01/2015

Pass a ViewDataDictionary object a the Partial View

@{

Html.RenderPartial("umbEventsWidget",

new ViewDataDictionary(this.ViewData)

{

{ "numberOfEvents", 5 }

});

}

Files: /Views/umbHomePage.cshtml

Page 23: Umbraco OktoberFest 2014

Task 4a (cont)

You are on page 2322/01/2015

Retrieve the ViewDataDictionary value

@{

var events = CurrentPage

.AncestorOrSelf(1)

.FirstChild("umbEventsOverview")

.Children.Take(NumberOfEvents());

}

Use a Razor function to make it tidy

@functions{

private int NumberOfEvents()

{

if (ViewData["numberOfEvents"] != null)

{

return (int)ViewData["numberOfEvents"];

}

//default 4

return 4;

}

}

Files: /Views/Partials/umbEventsWidget.cshtml

Page 24: Umbraco OktoberFest 2014

Task 5 – put it all together

You are on page 2422/01/2015

Only show events that have not yet passed, this is a job for a lambda expression and a function (convert ToDynamic if you want to)

@{

var events = Model.Content

.AncestorOrSelf(1)

.Children

.OfTypes("umbEventsOverview")

.First()

.Children(IsCurrentEvent)

.Take(NumberOfEvents())

.Select(x => x.AsDynamic());

}

Files: /Views/Partials/umbEventsWidget.cshtml

Page 25: Umbraco OktoberFest 2014

Task 6 – use a global Razor Class

You are on page 2522/01/2015

MyHelpers.cshtml in the App_Code folder

Helper – pass Umbraco Helper

@MyHelpers.RenderFooter(Umbraco)

Function

@MyHelpers.EasyDateFormat()

public static string EasyDateFormat()

{

return DateTime.Now.ToString("F");

}

Files: /App_Code/MyHelpers.cshtml

Page 26: Umbraco OktoberFest 2014

Task 7 – use a C# class in App_Code

You are on page 2622/01/2015

MyStuff.cs in the App_Code folder

Using the Function

@using UmbOktoberFest2014.App_Code

@MyStuff.EasyDateFormat()

The C# method

namespace UmbOktoberFest2014.App_Code

{

public static class MyStuff

{

public static string EasyDateFormat()

{

return DateTime.Now.ToString("F");

}

}

}

Files: /App_Code/MyStuff.cs

Page 27: Umbraco OktoberFest 2014

Task 8 - ToContentSet method

You are on page 2722/01/2015

Items within collections relate to their tree siblings not the other items in the collection, using the ToContentSet methods changes the below methods to be related to the items position within the collection.

.IsFirst()

.IsNotFirst()

.IsPosition ()

.IsModZero ()

.IsNotModZero ()

.IsNotPosition ()

.IsLast ()

.IsNotLast ()

.IsEven ()

.IsOdd ()

@{var events = Model

.Content

.AncestorOrSelf(1)

.Children

.OfTypes("umbEventsOverview").First()

.Children

.OrderBy("eventEndDate desc")

.ToContentSet();}<h2 class="major"><span>Upcoming events</span></h2>

<ul class="style2">@foreach (var eventItem in events){

<li><article class="is-post-summary">

<h3><a href="@eventItem.Url">@eventItem.IsFirst() @eventItem.Name</a></h3>

<ul class="meta"><li class="timestamp"></li>

</ul></article>

</li>}

</ul>Files: /Views/Partials/umbEventsWidget.cshtml

Page 28: Umbraco OktoberFest 2014

Task 9 - CsvContains

You are on page 2822/01/2015

Display a list of all news articles that have picked the event - Dynamic Model

@{

var releatedNews = CurrentPage

.AncestorOrSelf(1)

.FirstChild("umbNewsOverview")

.Children

.Where("relatedEvents.CsvContains(@0)",CurrentPage.Id.ToString())

.Take(4);

foreach (var newsItem in releatedNews)

{

<div class="3u">

<!-- Feature -->

<section class="is-feature">

<a href="@newsItem.Url" class="image image-full">

<img src="http://placehold.it/350x150" alt="" />

</a>

<h3><a href="@newsItem.Url">@newsItem.Name</a></h3>

</section>

</div>

}

}

Files: /Views/EventItem.cshtml

Page 29: Umbraco OktoberFest 2014

Task 9 (cont) - CsvContains

You are on page 2922/01/2015

Display a list of all news articles that have picked the event – Typed Model

@{

var releatedNews = Model.Content

.AncestorOrSelf(1)

.Children.OfTypes("umbNewsOverview").First()

.Children(x => x.GetPropertyValue<string>("relatedEvents")

.CsvContains(Model.Content.Id.ToString()))

.Take(4);

foreach (var newsItem in releatedNews)

{

<div class="3u">

<!-- Feature -->

<section class="is-feature">

<a href="@newsItem.Url" class="image image-full"><imgsrc="http://placehold.it/350x150" alt="" /></a>

<h3><a href="@newsItem.Url">@newsItem.Name</a></h3>

</section>

</div>

}

}

Files: /Views/EventItem.cshtml

Page 30: Umbraco OktoberFest 2014

Partial View Macros

You are on page 3022/01/2015

Macro’s were amazing (even XSLT ones), but we now have MVC with it’s native Partial Views so when should we use a Partial View Macro?

• When you want your content editor to be able insert a Macro in the RTE, Sir Trevor, v7.2 Grid or Macro picker property editor etc…

• Sites that have to use WebForms template so that they can use legacy user controls

Page 31: Umbraco OktoberFest 2014

Task 10: What about Media and Cropping?

You are on page 3122/01/2015

Lets convert the standard Image media type from “Upload” to “Image Cropper” use media pickers together

Page 32: Umbraco OktoberFest 2014

Value Converters & other questions

You are on page 3222/01/2015

If there is time left, then install the Value Converters Package, see what breaks and then fix it.

Documentation

https://github.com/Jeavon/Umbraco-Core-Property-Value-Converters/tree/v2

Page 33: Umbraco OktoberFest 2014

Crumpled Dog020 7739 5553 | [email protected] | www.crumpled-dog.com