dotnetnuke2sxc

In 2sxc Strongly Typed, How Can I Get the Page or File ID from a Hyperlink Field as an Int?


Okay, so I am playing with the Strongly Typed stuff now. Having the whole shebang working all at once in VS Code with IntelliSense and being able to use both field names as properties as well as .RazorTyped goodies like .Url("PageLink") really is the best of both (three?) worlds.

However, when you have a Link field (Hyperlink) in a 2sxc Content Type, internally it can store stuff like "file:441" or "page:33". So, I incorrectly assumed that it would be logical to expect .Int("PageLink") would return the page ID (the "33" part converted to an int). But as you can see...

  @foreach (var aPage in pages)
  {
    <pre>
PageLink: @aPage.PageLink
PageLink (url): @aPage.Url("PageLink")
PageLink (get): @aPage.Get("PageLink")
PageLink (int): @aPage.Int("PageLink")
    </pre>
  }

Which returns:

PageLink: https://staging.accuraty.us/Parks
PageLink (url): https://staging.accuraty.us/Parks
PageLink (get): page:34
PageLink (int): 0

Notice that asking for the .Int() gives you zero? It is just treating "page:34" as a string and not doing any parsing and falling back to int's default, 0.

Is there some way to easily get the Id without parsing or writing a method to handle processing the string returned by .Get()?


Solution

  • Now that 2sxc has Strongly Typed classes being generated from Content Types, you can easily move your parsing (method) to the class representation of your Content Type. Create your own Property on the (2sxc Generated) class!

    Maybe you think it should look like this:

    <p>PageLinkId: @aPage.PageLinkId</p>
    

    And return:

    PageLinkId: 34
    

    And it should be an int of course. Here is how you do it. Note that this was using 2sxc v17.06.03, which is LTS. So this should be a solid way to do this from now on.

    In your 2sxc App's folder, there is now a subfolder named AppCode/Data and in there you will find the GENERATED .cs classes for your content types (that you should NOT modify).

    My content type is named PagePlus. So in that folder I can see a file named, PagePlus.Generated.cs. I recommend you read/review that file, it has self-doc comments and you'll easily understand what is going on. The key thing you might not know yet is that 2sxc is actively monitoring your Content Types and on-save (when changed, or manually if you prefer), the *.Generated.cs file is recompiled to a DLL... but back to what we are doing. I added my own new file named PagePlus.cs in that same folder. This will not get overwritten, but it will get compiled (every time it is updated? or maybe not, after being initially created, I had to manually compile; there is a button for this in Manage App, Data, Copilot).

    Here is the complete contents of my new file /Portals/0/[MyAppName]/AppCode/Data/PagePlus.cs:

    // This is NOT auto-generated, instead, we (Accuraty) are extending the auto-generated class with our own (useful? time-saving?) custom properties and methods.
    // See also: https://go.2sxc.org/copilot-data
    
    // App/Edition: [MyAppName]/TBD
    // 20240412 JRF
    
    namespace AppCode.Data
    {
      public partial class PagePlus
      {
        /// <summary>
        /// PageLinkId as Int<br/>
        /// Returns the value 72 from 'file:72'
        /// </summary>
        public int PageLinkId => Destructure(_item.Get("PageLink").ToString()).Value;
    
        private (string Name, int Value) Destructure(string pair, char sep = ':')
        {
          string[] parts = pair.Split(sep);
          int number = 0;
          if (parts.Length == 2 && int.TryParse(parts[1], out number))
          {
            return (parts[0], number);
          }
          else
          {
            return ("Invalid pair?", number);
          }
        }
    
      }
    
    }
    

    This probably doesn't make much sense unless you are doing a large project and will need to get that ID as an Int often across multiple Views. But I wanted to share an example that shows how powerful it is now - you can have your Content Type have well named Properties with built in business logic, common time-saving utilities, and whatever seems useful.

    IMHO this is going to be pretty amazing (and fun).

    Another useful addition. After seeing the result used in code, we realize that .PageLinkId would be better understood if it were simply named, .PageId. Since there was no conflict, we added this additional one-liner to our partial class:

        /// <summary>
        /// PageId as Int<br/>
        /// Returns the value 72 from 'file:72'
        /// </summary>
        public int PageId => PageLinkId;
    

    So now our code is much more readable, here is an excerpt from a DDR Menu Razor Template that is getting all it's MegaMenu settings from a 2sxc Content Type named PagePlus.

    @{
      var pages = App.Data.GetAll<PagePlus>();
    }
    
     ...
      <div class="d-flex justify-content-around">
      @foreach (var node in rootChildren)
      {
        var currPage = pages.FirstOrDefault(p => p.PageId == node.TabId);