The best site map code I could make up with new Razor syntax in Umbraco still use that ugly cs html string
constructions. The cleaner / better option would be to use inline "helpers". Which
would be something like this:
@helper traversenodes(int nodeid) { if (c.Children.Count>0) { <ul> foreach (c in new Node(nodeid).Children) { <li>@c.Name traversenodes(c.Id); </li> } </ul> } } <h1>Sitemap</h1> @traversenodes(-1)
But - I could not get @helper to work in Umbraco. I don't know if there is some reason it would not work?
Mattew Abbot has declarative helpers working he says in his article, which Niels Hartvig mentions was an inspiration writing the Umbraco Razor engine. Could be Mattew updated his code later, he says "The updated project is attached" at the end of the article.
When I try (a very simple @helper) I get this error
System.ArgumentNullException: Value cannot be null. Parameter name: typeName at System.Web.Razor.Generator.CSharpCodeWriter.EmitStartConstructor(String typeName) at System.Web.Razor.Generator.RazorCodeGenerator.VisitSpan(HelperHeaderSpan span) at System.Web.Razor.Generator.RazorCodeGenerator.TryVisitSpecialSpanCore(Span span) at System.Web.Razor.Generator.RazorCodeGenerator.VisitSpan(Span span) at System.Web.Razor.Parser.ParserContext.EndBlock() at System.Web.Razor.Utils.DisposableAction.Dispose() at System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences, Boolean caseSensitive) at System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader input, ParserVisitor visitor) at umbraco.MacroEngines.Razor.RazorCompiler.Compile(String className, String template, Type modelType) in d:\TeamCity6\buildAgent\work\f90b8294889fbe14\umbraco.MacroEngines.Juno\Razor\RazorCompiler.cs:line 62 at
Hm... Cannot get pass the "Cannot use a lambda expression as an argument to a dynamically
dispatched operation without first casting it to a delegate or
expression tree type" error unfortunately.
DynamicNode
is not a part of Linq2Umbraco as far as I know. It came with Juno and the IMacroEngine. A great idea, adding some useful methods to nodes + dynamically adding
node properties as methods (as shown in Niels and Aarons examples). But
writing lambdas with dynamic objects obviously (?) arent as easy as one
would like.
Here's a script I converted to Razor (from a Python one) to point out some gotchas about Razor in Umbraco
@using System
@using System.Web @using umbraco.NodeFactory <h2>Lista här</h2> @{ string querystringId = umbraco.library.RequestQueryString("id"); int id; if (Int32.TryParse(querystringId, out id)) { var n = new Node(id); foreach (Node c in n.Children) { <li>@c.Name</li> } } }
1. The engine doesnt give you any namespaces by default. I add System and System.Web to be able to use common stuff like Parse and HtmlEncode. Namespaces cannot be added to web.config as I hoped (see earlier post in this thread). 2. You never would like to save a Razor script with syntax checking skipped. If the script does not validate it simply won't work. 3. HtmlEncoding does not happen automatically, so Swedish characters like åäö wont display correctly. I also tried HtmlEncoding my String, but that didnt work either (I got "?"'s). 4. The @ - syntax is not exactly the same as the one MVC3 is using, the most obvious thing is the oneliners, so @var x = "x" does not work. The easy fix is to enclose all c# code in @{ }, like in my example.
@* Helper function for the node traversion *@ @helper traverse(Node n) { @{ var c = n.GetChildNodes().Where(node=>!node.GetPropertyAsBoolean("umbracoNaviHide")).OrderBy(node=>node.Name); if (c.Any()) { <ul> @foreach (var cc in c) { <li><a href="@cc.Url"> @cc.Name </a> @traverse(cc) </li> } </ul> } } }
@* Render the sitemap *@ <h2>Sitemap</h2> @traverse(uQuery.GetRootNode())
sometimes it's indeed necessary to skip tests while saving a Razor macro. For example when a macro is querying for current node using nodeFactory or GetCurrent() using uQuery. For example to get the acestor on a certain depth (level):
var startNode = uQuery.GetCurrentNode().GetAncestorOrSelfNodes().FirstOrDefault(node => node.GetDepth()==2);
A not so pleasent experience is that the IMacroEngine compiler sometimes get stuck using a cached script. The only way I found around it is to restart the app (f example by touching web.config).
Playtime continues. I made complete login/logout macro script in Razor. Works pretty good actually. Would need some more attention before it makes a package though.
I've noticed that when moving the namespace registrations into the web.config, intellisense disappears when editing the cshtml file, is there a way keep intellisense but not have to put the using statements in each file ?
Razor @helper in Umbraco
The best site map code I could make up with new Razor syntax in Umbraco still use that ugly cs html string constructions. The cleaner / better option would be to use inline "helpers". Which would be something like this:
But - I could not get @helper to work in Umbraco. I don't know if there is some reason it would not work?
See also Scott Guthrie's sample of @helper here: http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx
Mattew Abbot has declarative helpers working he says in his article, which Niels Hartvig mentions was an inspiration writing the Umbraco Razor engine. Could be Mattew updated his code later, he says "The updated project is attached" at the end of the article.
}
}
}
When I try (a very simple @helper) I get this error
System.ArgumentNullException: Value cannot be null. Parameter name: typeName at System.Web.Razor.Generator.CSharpCodeWriter.EmitStartConstructor(String typeName) at System.Web.Razor.Generator.RazorCodeGenerator.VisitSpan(HelperHeaderSpan span) at System.Web.Razor.Generator.RazorCodeGenerator.TryVisitSpecialSpanCore(Span span) at System.Web.Razor.Generator.RazorCodeGenerator.VisitSpan(Span span) at System.Web.Razor.Parser.ParserContext.EndBlock() at System.Web.Razor.Utils.DisposableAction.Dispose() at System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences, Boolean caseSensitive) at System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader input, ParserVisitor visitor) at umbraco.MacroEngines.Razor.RazorCompiler.Compile(String className, String template, Type modelType) in d:\TeamCity6\buildAgent\work\f90b8294889fbe14\umbraco.MacroEngines.Juno\Razor\RazorCompiler.cs:line 62 at
Added to codeplex http://umbraco.codeplex.com/workitem/29817
Ah, great, it works fine in Umbraco 4.6.1!
Here's the full sitemap sample:
I needed to add the extra @{ } , could perhaps be made a bit more elegant?
oh, I forgot - add this in top of it all
(umbraco.presentation.nodeFactory is now obsolete)
Have you checked out AncestorOrDefault to see if you can just do it with lambda statements?
Hm... Cannot get pass the "Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type" error unfortunately.
Ahh yeah, you probably have to set the model type of the razor script. Damn...
Comment author was deleted
So it isn't possible to use lambda expressions?
Getting the same error when trying to do
Or am I doing something wrong?
Yeah, would be cool to use DynamicNode,
meanwhile here's a sample using uQuery (uComponents):
To minimize the code in a Razor script it should be possible to add the namespaces in web.config instead, right? http://www.west-wind.com/Weblog/posts/2287.aspx
Hi,
The GetDescendantNodes() method from the route node will recurse the whole tree - so to match the previous example GetChildNodes() would work.
DynamicNode looks interesting ! is that a part of LinqToUmbraco ?
Cheers,
Hendy
Hi oh, right, thanks. I just started with uQuery, really useful, thanks for sharing it :-)
A must read for all who, like me, need to learn more about it: http://ucomponents.codeplex.com/wikipage?title=uQuery .
DynamicNode is not a part of Linq2Umbraco as far as I know. It came with Juno and the IMacroEngine. A great idea, adding some useful methods to nodes + dynamically adding node properties as methods (as shown in Niels and Aarons examples). But writing lambdas with dynamic objects obviously (?) arent as easy as one would like.
Here's a script I converted to Razor (from a Python one) to point out some gotchas about Razor in Umbraco
1. The engine doesnt give you any namespaces by default. I add System and System.Web to be able to use common stuff like Parse and HtmlEncode. Namespaces cannot be added to web.config as I hoped (see earlier post in this thread).
2. You never would like to save a Razor script with syntax checking skipped. If the script does not validate it simply won't work.
3. HtmlEncoding does not happen automatically, so Swedish characters like åäö wont display correctly. I also tried HtmlEncoding my String, but that didnt work either (I got "?"'s).
4. The @ - syntax is not exactly the same as the one MVC3 is using, the most obvious thing is the oneliners, so @var x = "x" does not work. The easy fix is to enclose all c# code in @{ }, like in my example.
Hi Jonas,
Good to know that Razor supports namespace declarations defined in the web.config.
Cheers,
Hendy
I really liked to use DynamicNode, and tried to create one with:
However I get the error The type name 'Compilation' does not exist in the type 'umbraco.MacroEngines.RazorEngine' . And I cannot find that one?
Sorry Hendy - cannot. I corrected my post now. The Razor engine does not care about the added namesspaces in web.config.
Here's an corrected and updated sitemap sample with the use of both Where and OrderBy.
I continue my experiments with the Razor engine,
sometimes it's indeed necessary to skip tests while saving a Razor macro. For example when a macro is querying for current node using nodeFactory or GetCurrent() using uQuery. For example to get the acestor on a certain depth (level):
Last snippet for the day, top navigation (starting on level 2)
Playtime continues. I made complete login/logout macro script in Razor. Works pretty good actually. Would need some more attention before it makes a package though.
Cool - now it's possible to add default namespaces in web.config
Hi,
I've noticed that when moving the namespace registrations into the web.config, intellisense disappears when editing the cshtml file, is there a way keep intellisense but not have to put the using statements in each file ?
Thanks,
Hendy
Intellisense? How activate it in Umbraco?
is working on a reply...