Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jonas Eriksson 930 posts 1825 karma points
    Jan 06, 2011 @ 09:35
    Jonas Eriksson
    0

    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:

    @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?

    See also Scott Guthrie's sample of @helper here: http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 07, 2011 @ 09:43
    Jonas Eriksson
    0

    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.

    @using System.Collections.Generic
    @helper Title(string title) {
    @title
    }
    @helper DisplayItems(IEnumerable items) {
    @if(items != null) {
    @foreach(string item in items) {
  • @item

  • }
    }
    }

    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

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 12, 2011 @ 19:51
    Jonas Eriksson
    2

    Ah, great, it works fine in Umbraco 4.6.1! 

    Here's the full sitemap sample:

    @helper traverse(int nodeid) { 
     @{
      Node n = new Node(nodeid);
      if (n.Children.Count>0) {
       <ul>
        @foreach (Node c in n.Children) {
        <li>
         @c.Name  
         @traverse(c.Id)
        </li>
        }
       </ul>  
      }
     }
    }
    <h1>Sitemap</h1>
    @traverse(-1)

    I needed to add the extra @{ } , could perhaps be made a bit more elegant? 

     

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 12, 2011 @ 19:53
    Jonas Eriksson
    1

    oh, I forgot - add this in top of it all

    @using umbraco.NodeFactory           

    (umbraco.presentation.nodeFactory is now obsolete)

  • Aaron Powell 1708 posts 3046 karma points c-trib
    Jan 13, 2011 @ 02:56
    Aaron Powell
    0

    Have you checked out AncestorOrDefault to see if you can just do it with lambda statements?

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 13, 2011 @ 06:55
    Jonas Eriksson
    0

    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.

  • Aaron Powell 1708 posts 3046 karma points c-trib
    Jan 13, 2011 @ 07:31
    Aaron Powell
    0

    Ahh yeah, you probably have to set the model type of the razor script. Damn...

  • Comment author was deleted

    Jan 14, 2011 @ 13:02

    So it isn't possible to use lambda expressions?

    Getting the same error when trying to do

    @foreach (var p in @Model.Children.Where(c => c.umbracoNaviHide != "1"))
    {
       <li>@p.Name</li>
    }

    Or am I doing something wrong?

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 15, 2011 @ 04:27
    Jonas Eriksson
    1

    Yeah, would be cool to use DynamicNode,

    meanwhile here's a sample using uQuery (uComponents):

    @using uComponents.Core
    @using uComponents.Core.uQueryExtensions
    @using System.Linq
    @using umbraco.presentation.nodeFactory
    @helper traverse(Node n)
    {
     @{
       var c = n.GetDescendantNodes().Where(node=>!node.GetPropertyAsBoolean("umbracoNaviHide"));
       if (c.Any())
       {
        <ul>
        @foreach (var cc in c)
        {
            <li><a href="@cc.Url">
                @cc.Name
            </a>
                @traverse(cc)
            </li>
         }
        </ul>
        }
       }
    }
    @traverse(uQuery.GetRootNode())
  • Jonas Eriksson 930 posts 1825 karma points
    Jan 15, 2011 @ 09:14
    Jonas Eriksson
    0

    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

  • Hendy Racher 863 posts 3849 karma points MVP 2x admin c-trib
    Jan 15, 2011 @ 16:40
    Hendy Racher
    0

    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

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 15, 2011 @ 18:40
    Jonas Eriksson
    1

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 09:06
    Jonas Eriksson
    1

    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&auml;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.

  • Hendy Racher 863 posts 3849 karma points MVP 2x admin c-trib
    Jan 16, 2011 @ 09:21
    Hendy Racher
    0

    Hi Jonas,

    Good to know that Razor supports namespace declarations defined in the web.config.

    Cheers,

    Hendy

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 09:23
    Jonas Eriksson
    0

    I really liked to use DynamicNode, and tried to create one with:

    @using umbraco.MacroEngines
    @using umbraco.NodeFactory
    @{
    var dn = new DynamicNode(new Node(id));
    }

    However I get the error The type name 'Compilation' does not exist in the type 'umbraco.MacroEngines.RazorEngine' . And I cannot find that one?

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 09:37
    Jonas Eriksson
    0

    Sorry Hendy - cannot. I corrected my post now. The Razor engine does not care about the added namesspaces in web.config.

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 11:20
    Jonas Eriksson
    1

    Here's an corrected and updated sitemap sample with the use of both Where and OrderBy.

    @using System
    @using System.Web
    @using uComponents.Core
    @using uComponents.Core.uQueryExtensions
    @using System.Linq
    @using umbraco.presentation.nodeFactory

    @* 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())

     

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 16:07
    Jonas Eriksson
    0

    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):

     

      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).

     

  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 17:37
    Jonas Eriksson
    0

    Last snippet for the day, top navigation (starting on level 2)

    @using uComponents.Core
    @using uComponents.Core.uQueryExtensions
    @using System.Linq
    
    @{
      var startNode = uQuery.GetCurrentNode().GetAncestorOrSelfNodes().FirstOrDefault(node => node.GetDepth()==2);
      var children = startNode.GetChildNodes().Where(node=>!node.GetPropertyAsBoolean("umbracoNaviHide"));
        <ul>
        @foreach (var c in children)
        {
          string css="";
          if (c.Id == uQuery.GetCurrentNode().Id) css="class =\"selected\"";
          <li @css>      
            <a href="@c.Url">
                @c.Name
            </a>
          </li> 
        }
        </ul>
    }
    
  • Jonas Eriksson 930 posts 1825 karma points
    Jan 16, 2011 @ 22:24
    Jonas Eriksson
    1

    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.

    @using System.Web  
    @using System.Web.Security
    @helper LoginForm()
    {
      <form>
      <label for="name">Username:</label>
      <input type="text" id="username" name="username"/>
      <label for="password">Password:</label>
      <input type="password" id="password" name="password"/>
      <input type="submit" id="submit" name="submit" value="login"/>
      </form>
    }
    @helper LogoutForm()
    {
      <form><input type="submit" id="submit" name="submit" value="logout"/></form>
    }
    @{
      var request = HttpContext.Current.Request;
      var isSubmitLogin = (request.HttpMethod=="POST" && request.Form["submit"]=="login");
      var isSubmitLogout = (request.HttpMethod=="POST" && request.Form["submit"]=="logout");
      var currentUser = Membership.GetUser();
     
      if (currentUser!=null)
       {   
        if (!isSubmitLogout)
         {
           <p>Logged in : @currentUser.UserName</p>
           @LogoutForm()
         }
        else
         {
           FormsAuthentication.SignOut();
           FormsAuthentication.RedirectToLoginPage();
         }
       }
      if (currentUser==null)
       {
        if (!isSubmitLogin) 
         {
           @LoginForm()
          }
        else
         {
          string username=request.Form["username"];
          string password=request.Form["password"];
          if (Membership.ValidateUser(username, password))
          {
            FormsAuthentication.RedirectFromLoginPage(username, true);
          }
          else
          {
            <p> Login failed for @username</p>
            @LoginForm()
          }
         }      
        }
      }
  • Jonas Eriksson 930 posts 1825 karma points
    Feb 24, 2011 @ 19:15
    Jonas Eriksson
    1

    Cool - now it's possible to add default namespaces in web.config

      <system.web.webPages.razor>
    <host factoryType="umbraco.MacroEngines.RazorUmbracoFactory, umbraco.MacroEngines" />
    <pages pageBaseType="umbraco.MacroEngines.DynamicNodeContext">
    <namespaces>
    <add namespace="Microsoft.Web.Helpers" />
    <add namespace="umbraco" />
    <add namespace="Examine" />
    <add namespace="umbraco.MacroEngines" />
    <add namespace="uComponents.Core" />
    <add namespace="uComponents.Core.uQueryExtensions" />
    </namespaces>
    </pages>
  • Hendy Racher 863 posts 3849 karma points MVP 2x admin c-trib
    Mar 09, 2012 @ 16:33
    Hendy Racher
    0

    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

  • Biagio Paruolo 1594 posts 1825 karma points c-trib
    Apr 24, 2012 @ 16:06
    Biagio Paruolo
    0

    Intellisense? How activate it in Umbraco?

Please Sign in or register to post replies

Write your reply to:

Draft