Umbraco OktoberFest 2014
-
Upload
jeavon-leopold -
Category
Technology
-
view
292 -
download
12
Transcript of Umbraco OktoberFest 2014
Razor Revealed
Jeavon Leopold | Twitter: @crumpled_Jeavon
We are Crumpled Dog
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?
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
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)
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
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
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
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)
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
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
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
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)
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())
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Crumpled Dog020 7739 5553 | [email protected] | www.crumpled-dog.com